自作関数のリエントラント性について

RL78/G14(R5F104LLAFB#V0)で、C言語の自作関数を使用しておりますが、この関数をメインルーチンと割り込みルーチンの両方で使用しております。

関数内ではグローバル変数を使用しておらず、リエントラントになっていると考えております。

 

void mmcopy( _UBYTE *d5, _UBYTE *s5, _UWORD n)
{ _UWORD i;
for(i=0;i<n;i++)
*(d5+i) = *(s5+i);
}

 

この関数がメインと割り込みで競合した場合、カウンタiはスタック退避されて戻れると認識しておりますが、E1エミュレータを接続し、連続運転していると

RESF=10hのリセットが発生します。OCDトレースを見るとこの関数で無限ループに陥っているように見えます。

RL78では上記のような関数はリエントラントにはならないのでしょうか?

Parents
  • mmcopy関数の呼び出し元を全てご確認してはどうでしょうか?
    ソース入力ミスで*d5とか*s5の引数部分に呼出し元のauto変数に&つけて、スタックアドレスを渡してスタック領域を破損させて暴走とか?
  • fujita nozomuさん

    コメントありがとうございます。

    > _stkinit を有効化してスタック領域を初期化するようにした後、領域外への書き込みがあるかを確認することは有効です。

    ありがとうございます。試してみます。

    > 副業不可なので有償でのそれはお受けできません。

    承知致しました。

    のーしゃんさん

    コメントありがとうございます。

    > mmcopy関数の呼び出し元を全てご確認してはどうでしょうか?

    ご指摘について、確かにmmcopyの呼び出し元で引数の扱い方がかなり怪しい部分があります。
    そこを重点的に調べてみます。
  • のーしゃんさん

    ご指摘頂いたauto変数に&をつけているところがありました。

    void Old_MX( _UBYTE pg )
    {
    volatile _UBYTE lp,cf,txsz;
    volatile _UBYTE c1;
    volatile _UBYTE pp;
    volatile _UBYTE bytp,bitp,bitd;
    volatile _UBYTE Dswrty;
    volatile _UBYTE tw[80];
    volatile struct TNKDATA tf; // 変化比較用バッファ
    ~中略~
    mmcopy( (_UBYTE *)&tw[5],(_UBYTE *)"thr=000 -- ",11);


    アセンブルリストを確認したところ、

    00002F1F 8596 .BB@LABEL@23_89: ; if_break_bb349
    00002F1F 8597 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 2659
    00002F1F 340B00 8598 movw de, #0x000B
    00002F22 320000 8599 movw bc, #SMRLW(.STR@11034)
    00002F25 AEF8 8600 movw ax, sp
    00002F27 041500 8601 addw ax, #0x0015
    00002F2A FC000000 8602 call !!_mmcopy

    となっておりました。axは*d5、bcは*s5、deはnとなっております。アセンブラの読解力が乏しいので正しく理解できておりませんが、このようなコードがスタックアドレスを破壊しているのでしょうか?
    しかしtwは配列のため、&をつけてもも間違いではないと理解しております。

    勉強不足で申し訳ありませんが、上記が問題なのでしょうか?
  • チョコです。

    アセンブラでの動作についてコメントさせてもらいます。

    2F25 AEF8 8600 movw ax, sp

    2F27 041500 8601 addw ax, #0x0015

    でaxはスタック領域のスタックトップから21(0x15)バイト目を指しています。

    axがディスティネーション側(転送先)なら,もろにスタック領域を破壊します。

    (axが転送元で,スタックに積まれているデータを転送するならわからないではないですが。)

     

    以上

  • > axがディスティネーション側(転送先)なら,もろにスタック領域を破壊します。
    > (axが転送元で,スタックに積まれているデータを転送するならわからないではないですが。)

    AX は転送先ですが、スタックフレーム上に設けられたローカル変数 _UBYTE tw[80] の 5バイト目のアドレスで問題ない筈です。

    > mmcopy( (_UBYTE *)&tw[5],(_UBYTE *)"thr=000 -- ",11);

    は tw[] に出力したい文字列を作成する処理の一部分なのだと思いますが、「何バイト目から何バイト "~" をコピーして、その後ろに "…" をコピーして、最後に '\0' を書いて、」等やってるとミスも生じ易くなるので、素直に sprintf() とか使われた方が良いでしょう。コードも簡潔になるので保守性も向上します。
  • こんばんは。

    mmcopy( (_UBYTE *)&tw[5],(_UBYTE *)"thr=000 -- ",11);は問題ないと思います。

    気になるのはauto変数にvolatileが宣言されていることです。
    この関数内でmmcopyのように外部関数にauto変数のアドレスを伝えている場合で
    それが割り込み関連の関数の場合にOld_MX関数を抜けた後に伝えたアドレスに対して
    ライトアクセスが発生するとスタックを壊すことになります。
    volatile宣言はstaticな変数に使用することが普通です。
    ご確認ください。
Reply
  • こんばんは。

    mmcopy( (_UBYTE *)&tw[5],(_UBYTE *)"thr=000 -- ",11);は問題ないと思います。

    気になるのはauto変数にvolatileが宣言されていることです。
    この関数内でmmcopyのように外部関数にauto変数のアドレスを伝えている場合で
    それが割り込み関連の関数の場合にOld_MX関数を抜けた後に伝えたアドレスに対して
    ライトアクセスが発生するとスタックを壊すことになります。
    volatile宣言はstaticな変数に使用することが普通です。
    ご確認ください。
Children
  • チョコさん
    fujita nozomuさん
    のーしゃんさん

    コメントありがとうございます。

    volatileについてはこの場合確かに不要かもしれません。
    過去の経験からある種トラウマ的にすべてにvolatileを付ける習慣がありまして・・・
    Old_MXはメイン関数です。
    皆様のコメントから改善点はあるものの、これが致命的なバグではないと判断します。

    ではもう一つご覧頂きたいのですが、

    00006574 18146 _Montx:
    00006574 18147 .STACK _Montx = 10
    00006574 18148 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4831
    00006574 2006 18149 subw sp, #0x06
    00006576 B802 18150 movw [sp+0x02], ax
    00006578 62 18151 mov a, c
    00006579 9800 18152 mov [sp+0x00], a
    0000657B 302201 18153 movw ax, #0x0122
    0000657E 18154 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4833
    0000657E B804 18155 movw [sp+0x04], ax
    00006580 18156 .BB@LABEL@43_1: ; entry.split
    00006580 18157 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4835
    00006580 AF0000 18158 movw ax, !LOWW(_Mrp)
    00006583 420000 18159 cmpw ax, !LOWW(_Mwp)
    00006586 DF00 18160 bnz $.BB@LABEL@43_8
    00006588 18161 .BB@LABEL@43_2: ; if_then_bb
    00006588 18162 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 0
    00006588 F6 18163 clrw ax
    00006589 18164 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4838
    00006589 BF0000 18165 movw !LOWW(_Mwp), ax
    0000658C AF0000 18166 movw ax, !LOWW(_Mwp)
    0000658F BF0000 18167 movw !LOWW(_Mrp), ax
    00006592 18168 .BB@LABEL@43_3: ; if_then_bb.split
    00006592 18169 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4839
    00006592 8800 18170 mov a, [sp+0x00]
    00006594 318E 18171 shrw ax, 8+0x00000
    00006596 14 18172 movw de, ax
    00006597 A802 18173 movw ax, [sp+0x02]
    00006599 12 18174 movw bc, ax
    0000659A 300000 18175 movw ax, #LOWW(_MTx)
    0000659D FC000000 18176 call !!_mmcopy
    000065A1 18177 .BB@LABEL@43_4: ; if_then_bb.split1
    000065A1 18178 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4840
    000065A1 8800 18179 mov a, [sp+0x00]
    000065A3 318E 18180 shrw ax, 8+0x00000
    000065A5 BF0000 18181 movw !LOWW(_Mwp), ax
    000065A8 18182 .BB@LABEL@43_5: ; if_then_bb.split2
    000065A8 18183 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4841
    000065A8 8800 18184 mov a, [sp+0x00]
    000065AA 318E 18185 shrw ax, 8+0x00000
    000065AC BF0000 18186 movw !LOWW(_U1_Txsz), ax
    000065AF 18187 .BB@LABEL@43_6: ; if_then_bb.split3
    000065AF 18188 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4842
    000065AF FC000000 18189 call !!_R_UART1_Start
    000065B3 18190 .BB@LABEL@43_7: ; if_then_bb.split4
    000065B3 18191 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4843
    000065B3 8800 18192 mov a, [sp+0x00]
    000065B5 318E 18193 shrw ax, 8+0x00000
    000065B7 12 18194 movw bc, ax
    000065B8 300000 18195 movw ax, #LOWW(_MTx)
    000065BB 020000 18196 addw ax, !LOWW(_Mrp)
    000065BE FC000000 18197 call !!_R_UART1_Send
    000065C2 1006 18198 addw sp, #0x06
    000065C4 D7 18199 ret
    000065C5 18200 .BB@LABEL@43_8: ; if_else_bb
    000065C5 18201 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4848
    000065C5 8800 18202 mov a, [sp+0x00]
    000065C7 318E 18203 shrw ax, 8+0x00000
    000065C9 12 18204 movw bc, ax
    000065CA A804 18205 movw ax, [sp+0x04]
    000065CC 220000 18206 subw ax, !LOWW(_Mwp)
    000065CF 43 18207 cmpw ax, bc
    000065D0 61D300 18208 bnh $.BB@LABEL@43_11
    000065D3 18209 .BB@LABEL@43_9: ; if_then_bb29
    000065D3 18210 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4850
    000065D3 8800 18211 mov a, [sp+0x00]
    000065D5 318E 18212 shrw ax, 8+0x00000
    000065D7 14 18213 movw de, ax
    000065D8 A802 18214 movw ax, [sp+0x02]
    000065DA 12 18215 movw bc, ax
    000065DB 300000 18216 movw ax, #LOWW(_MTx)
    000065DE 020000 18217 addw ax, !LOWW(_Mwp)
    000065E1 FC000000 18218 call !!_mmcopy
    000065E5 18219 .BB@LABEL@43_10: ; if_then_bb29.split
    000065E5 18220 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4851
    000065E5 8800 18221 mov a, [sp+0x00]
    000065E7 318E 18222 shrw ax, 8+0x00000
    000065E9 020000 18223 addw ax, !LOWW(_Mwp)
    000065EC BF0000 18224 movw !LOWW(_Mwp), ax
    000065EF 1006 18225 addw sp, #0x06
    000065F1 D7 18226 ret
    000065F2 18227 .BB@LABEL@43_11: ; if_else_bb40
    000065F2 18228 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4855
    000065F2 DB0000 18229 movw bc, !LOWW(_Mwp)
    000065F5 390000FF 18230 mov LOWW(_MTx)[bc], #0xFF
    000065F9 18231 .BB@LABEL@43_12: ; if_else_bb40.split
    000065F9 18232 .LINE "C:/Users/Ando/Documents/VCD-30/Soft/20190910A_VCD-30G14/VCD-30G14/r_main.c", 4856
    000065F9 A20000 18233 incw !LOWW(_Mwp)
    000065FC 1006 18234 addw sp, #0x06
    000065FE D7 18235 ret

    こちらのCソースは

    /************************************************/
    /* モニターデータの送信 */
    /* Mainルーチン内で実行すること */
    /************************************************/
    void Montx( _UBYTE *mt ,_UBYTE mln )
    {
    _UWORD mx=sizeof(MTx)-10;

    if( Mrp==Mwp )
    {
    //----- 送信データ残 無し
    Mrp = Mwp = 0;
    mmcopy( (_UBYTE *)MTx, (_UBYTE *)mt, mln );
    Mwp =mln;
    U1_Txsz = mln;
    R_UART1_Start();
    R_UART1_Send((uint8_t *)(MTx+Mrp), mln );
    }
    else
    {
    //----- 送信中
    if((mx-Mwp)>mln)
    {
    mmcopy( (_UBYTE *)(MTx+Mwp), (_UBYTE *)mt, mln );
    Mwp += mln;
    }
    else
    {
    *(MTx+Mwp)= 0xff; // Txbuff Overflow Code
    Mwp += 1;
    }
    }
    }

    です。MTx、Mwp、Mrpはグローバル変数で、

    volatile _UBYTE MTx[300];
    volatile _UWORD Mwp;
    volatile _UWORD Mrp;

    となっております。
    MTxは配列のため、[]を付けない場合はMTx[0]のアドレスとなるはずと理解しておりますが、アセンブルリストを見るとイミディエイトになっております。

    0000659A 300000 18175 movw ax, #LOWW(_MTx)

    これが問題でしょうか?
  • > MTxは配列のため、[]を付けない場合はMTx[0]のアドレスとなるはずと理解しておりますが、アセンブルリストを見るとイミディエイトになっております。
    >
    > 0000659A 300000 18175 movw ax, #LOWW(_MTx)
    >
    > これが問題でしょうか?

    大域変数は静的に配置されアドレスは直値で参照できるのでそこは問題ないと思います。

  • fujita nozomuさん

    コメントありがとうございます。

    だんだんアセンブラも読めるようになってきたのでわかりました。

    昨日類似の事象が発生しておりました。

    スタック初期化ルーチンを有効にした後、ブレークポイントの設定を変えなかったせいか、おかしなところでブレークが掛かっており、RESFも0でしたが、スタック終了アドレス付近を確認したところ、モニターポート出力(Montx)のメッセージで浸食されておりました。

    この関数に問題があるようですが、ある特定の条件がそろわないと発生しないようです。

  • こちらのCソースは
    
    /************************************************/
    /* モニターデータの送信 */
    /* Mainルーチン内で実行すること */
    /************************************************/
    void Montx( _UBYTE *mt ,_UBYTE mln )
    {
        _UWORD mx=sizeof(MTx)-10;
    
        if( Mrp==Mwp )
        {
            //----- 送信データ残 無し
            Mrp = Mwp = 0;
            mmcopy( (_UBYTE *)MTx, (_UBYTE *)mt, mln );
            Mwp =mln;
            U1_Txsz = mln;
            R_UART1_Start();
            R_UART1_Send((uint8_t *)(MTx+Mrp), mln );
        }
        else
        {
            //----- 送信中
            if((mx-Mwp)>mln)
            {
                mmcopy( (_UBYTE *)(MTx+Mwp), (_UBYTE *)mt, mln );
                Mwp += mln;
            }
            else
                {
                *(MTx+Mwp)= 0xff; // Txbuff Overflow Code
                Mwp += 1;
            }
        }
    }
    
    です。MTx、Mwp、Mrpはグローバル変数で、
    
    volatile _UBYTE MTx[300];
    volatile _UWORD Mwp;
    volatile _UWORD Mrp;
    
    

    について、

    • MTx[] は UART への送信バッファ
    • Mwp は MTx[] への書き込みインデクス
    • 実際の UART への送信は割り込み処理で行われ、MTx[] に格納されている内容を Mrp を参照して読み込み UART のデータレジスタへ書き込む。Mrp の値は UART へ送信した分だけ進む。Mrp の値が進み Mrp == Mwp となった場合 MTx[] は空ということなので割り込み処理を停止する

    という想定で考えると、

        if( Mrp==Mwp )
    

    が成立しない判定を行った直後のタイミングで割り込み処理により MTx[] に格納された最後の 1バイトを UART へ送信し、MTx[] の内容が空となり、UART 関連の割り込み処理が止められた場合、else 以降の処理

        else
        {
            //----- 送信中
            if((mx-Mwp)>mln)
            {
                mmcopy( (_UBYTE *)(MTx+Mwp), (_UBYTE *)mt, mln );
                Mwp += mln;
            }
            else
                {
                *(MTx+Mwp)= 0xff; // Txbuff Overflow Code
                Mwp += 1;
            }
        }
    }
    

    送信データなりエラーを表す(?)0xff なりの値が MTx[] へ書き込まれ、以降 Montx() が呼ばれる度にちょっとづつ MTx[] へのデータの蓄積はされ続け、その内 MTx[] の領域は溢れて宜しくないことになりそうな気はします。

  • fujita nozomuさん

    コメントありがとうございます。

    > 実際の UART への送信は割り込み処理で行われ、

    割り込み処理ではなく、上記のR_UART1_Send関数によって行われております。

    /***********************************************************************************************************************
    * Function Name: R_UART1_Send
    * Description : This function sends UART1 data.
    * Arguments : tx_buf -
    * transfer buffer pointer
    * tx_num -
    * buffer size
    * Return Value : status -
    * MD_OK or MD_ARGERROR
    ***********************************************************************************************************************/
    MD_STATUS R_UART1_Send(uint8_t * const tx_buf, uint16_t tx_num)
    {
    MD_STATUS status = MD_OK;

    if (tx_num < 1U)
    {
    status = MD_ARGERROR;
    }
    else
    {
    gp_uart1_tx_address = tx_buf;
    g_uart1_tx_count = tx_num;
    STMK1 = 1U; /* disable INTST1 interrupt */
    TXD1 = *gp_uart1_tx_address;
    gp_uart1_tx_address++;
    g_uart1_tx_count--;
    STMK1 = 0U; /* enable INTST1 interrupt */
    }

    return (status);
    }

    このコードでは単に送信バッファにデータをセットするだけで、実際の送信は割り込みで行われるのでしょうか?

    APIリファレンスでは

    本 API 関数では、引数 tx_buf で指定されたバッファから 1 バイト単位のUART 送信を
    引数 tx_num で指定された回数だけ繰り返し行います。

    と記載されております。

     

    また、残データがある場合、r_uart1_callback_sendendで送信しております。

    /***********************************************************************************************************************
    * Function Name: r_uart1_callback_sendend
    * Description : This function is a callback function when UART1 finishes transmission.
    * Arguments : None
    * Return Value : None
    ***********************************************************************************************************************/
    static void r_uart1_callback_sendend(void)
    {
    /* Start user code. Do not edit comment generated here */

    _SWORD tln;
    Mrp +=U1_Txsz;
    if(Mwp==Mrp)
    Mrp = Mwp = 0;
    else
    {
    tln = Mwp-Mrp;
    if(tln<0)
    {
    Mrp=Mwp=0;
    }
    else
    {
    R_UART1_Start();
    if( *(MTx+Mrp)== 0xff )
    {
    R_UART1_Send((uint8_t *)"<ov>", 4 );
    U1_Txsz=1;
    }
    else
    { R_UART1_Send((uint8_t *)(MTx+Mrp), tln );
    U1_Txsz = tln;
    }
    }
    }
    /* End user code. Do not edit comment generated here */
    }

     

    > MTx[] の領域は溢れて宜しくないことになりそうな気はします。

    3/13に発生したWDTRFの時のMTx(0xf50ec-0xf5218)を確認したところ、0xf5218を超える領域に送信テキストが書かれておりました。

    ご指摘の通り溢れておりました。

  • 割り込み処理ではなく、上記のR_UART1_Send関数によって行われております。
    
    /***********************************************************************************************************************
    * Function Name: R_UART1_Send
    * Description  : This function sends UART1 data.
    * Arguments    : tx_buf -
    *                    transfer buffer pointer
    *                tx_num -
    *                    buffer size
    * Return Value : status -
    *                    MD_OK or MD_ARGERROR
    ***********************************************************************************************************************/
    MD_STATUS R_UART1_Send(uint8_t * const tx_buf, uint16_t tx_num)
    {
        MD_STATUS status = MD_OK;
    
        if (tx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            gp_uart1_tx_address = tx_buf;
            g_uart1_tx_count = tx_num;
            STMK1 = 1U;    /* disable INTST1 interrupt */
            TXD1 = *gp_uart1_tx_address;
            gp_uart1_tx_address++;
            g_uart1_tx_count--;
            STMK1 = 0U;    /* enable INTST1 interrupt */
        }
    
        return (status);
    }
    
    このコードでは単に送信バッファにデータをセットするだけで、実際の送信は割り込みで行われるのでしょうか?
    
    

    コード生成ツールで作成したコード通りであれば、最初の 1文字をその関数の

    
            TXD1 = *gp_uart1_tx_address;
    
    

    で TXD1 に書き込んだ以降、残りの文字は

    
    /***********************************************************************************************************************
    * Function Name: r_uart1_interrupt_send
    * Description  : This function is INTST1 interrupt service routine.
    * Arguments    : None
    * Return Value : None
    ***********************************************************************************************************************/
    static void __near r_uart1_interrupt_send(void)
    {
        if (g_uart1_tx_count > 0U)
        {
            TXD1 = *gp_uart1_tx_address;
            gp_uart1_tx_address++;
            g_uart1_tx_count--;
        }
        else
        {
            r_uart1_callback_sendend();
        }
    }
    
    

    上記の割り込み処理で書き込む動作となります。

    
    また、残データがある場合、r_uart1_callback_sendendで送信しております。
    
    /***********************************************************************************************************************
    * Function Name: r_uart1_callback_sendend
    * Description : This function is a callback function when UART1 finishes transmission.
    * Arguments : None
    * Return Value : None
    ***********************************************************************************************************************/
    static void r_uart1_callback_sendend(void)
    {
        /* Start user code. Do not edit comment generated here */
        
        _SWORD tln;
        Mrp +=U1_Txsz;
        if(Mwp==Mrp)
            Mrp = Mwp = 0;
        else
        {
            tln = Mwp-Mrp;
            if(tln<0)
            {
                Mrp=Mwp=0;
            }
            else
            {
                R_UART1_Start();
                if( *(MTx+Mrp)== 0xff )
                {
                    R_UART1_Send((uint8_t *)"", 4 );
                    U1_Txsz=1;
                }
                else
                { R_UART1_Send((uint8_t *)(MTx+Mrp), tln );
                    U1_Txsz = tln;
                }
            }
        }
        /* End user code. Do not edit comment generated here */
    }
    
    

    コード生成ツールで生成したコードでは r_uart1_callback_sendend() が呼ばれるのは残データがない場合です。r_uart1_callback_sendend() の動作は、Mrp に U1_Txsz の値が加えられ、if(Mwp==Mrp) が成立せず、tln = Mwp-Mrp で tln に負の値が代入されるので、

    
            if(tln<0)
            {
                Mrp=Mwp=0;
            }
    
    

    が実行されるのみでこの関数は実行を終えるので、R_UART1_Send() が呼ばれることはなく、UART1 の割り込み処理は停止し、先に書いた通り MTx[] の領域は溢れることになると思います。

  • fujita nozomuさん

    コメントありがとうございます。

    > 上記の割り込み処理で書き込む動作となります。

    失礼致しました。仰る通りです。見落としておりました。

    > が実行されるのみでこの関数は実行を終えるので、R_UART1_Send() が呼ばれることはなく、

    r_uart1_callback_sendend()の

               if( *(MTx+Mrp)== 0xff )

    でブレークポイントを仕掛けると停止し、ステップ実行すると

               { R_UART1_Send((uint8_t *)(MTx+Mrp), tln );

    を実行しております。

    > コード生成ツールで生成したコードでは r_uart1_callback_sendend() が呼ばれるのは残データがない場合です。

    Montx内でR_UART1_Sendを実行している

    R_UART1_Send((uint8_t *)(MTx+Mrp), mln );

    は、この時実質Mrp=0なのでMtxの先頭からmlnまでを送信バッファに蓄積し、mln送信し終えたところでr_uart1_callback_sendendが呼ばれると理解しております。これが呼ばれる前に再びMontxが呼ばれると、else以降の

    if((mx-Mwp)>mln)

    {

    mmcopy( (_UBYTE *)(MTx+Mwp), (_UBYTE *)mt, mln );

    Mwp += mln;

    が実行され、MTxは最初のmlnに加えて今回のmln分だけ成長します。従ってこの後r_uart1_callbadk_sendend()が呼ばれた際にMrp+=U1_Txszされたとしても、Mwp>Mrpとなる場合があります。

    従って、たちまちMTxが溢れるということはありません。もしご指摘の通りr_uart1_callback_sendendでR_UART1_Sendが呼ばれることがなければもっと高頻度で溢れが発生するものと思われます。

    何かもう一つトリガになるものがあると推測しております。

  • > 何かもう一つトリガになるものがあると推測しております。

    http://japan.renesasrulz.com/cafe_rene/f/forum18/6274/thread/34781#34781

     で書いてる通り

        if( Mrp==Mwp )
    

    が成立しない判定を行った直後のタイミングで割り込み処理により MTx[] に格納された最後の 1バイトを UART へ送信し、MTx[] の内容が空となり、UART 関連の割り込み処理が止められた場合、

    タイミングを限定した条件での話をしてますよ。

  • fujita nozomuさん

    コメントありがとうございます。

    > タイミングを限定した条件での話をしてますよ。
    失礼致しました。理解が悪くてすみません。
    つまり、

    ①Montx関数が呼ばれ、その時点でMrp==Mwpが成立していない。
    ②if(Mrp==Mwp)の判定がFalseとなった後、最後の1文字が送信されr_uart1_interrupt_send()が呼ばれる。
    ③r_uart1_interrupt_send()内のif (g_uart1_tx_count > 0U)がFalse となり、r_uart1_callback_sendend()が呼ばれる。
    ④r_uart1_callback_sendend()内のif(Mwp==Mrp)が成立し、Mrp=Mwp=0となる。
    ⑤割り込みから復帰し、Montx内のelse以降が実行され、この時点でMrp=0、Mwp=1 or mlnとなる。
    ⑥以降、Montx関数が呼ばれる度にif(Mrp==Mwp)が成立せず、else文以降を実行し続けてMTxが溢れる。

    ということですね。かなりレアな条件ですね。

    Montx関数内を割り込み禁止にするのは本末転倒な気がします。
    かといって、else文以降のどこで送信割り込みが発生するかわからないため、闇雲にR_UART1_Sendをちりばめるのもまた本末転倒な気がします。
    もともと、Renesasのサンプルプログラムを見ても、送信完了までNopで待つのが最もシンプルなやり方ですが、こちらの機能は本来デバッグ用モニター出力のため、本来の機能を損なわないようにこのような優先順位を落とした処理を行っております。それが原因で本来の機能を損ねるのは不本意です。

    原因が特定できたのは大きな前進です。
    アドバイス頂き大変ありがとうございました。
  • > Montx関数内を割り込み禁止にするのは本末転倒な気がします。

    コード生成されたコードでデータ送信をセットする関数 R_UART1_Send() の中で

    
    /***********************************************************************************************************************
    * Function Name: R_UART1_Send
    * Description  : This function sends UART1 data.
    * Arguments    : tx_buf -
    *                    transfer buffer pointer
    *                tx_num -
    *                    buffer size
    * Return Value : status -
    *                    MD_OK or MD_ARGERROR
    ***********************************************************************************************************************/
    MD_STATUS R_UART1_Send(uint8_t * const tx_buf, uint16_t tx_num)
    {
        MD_STATUS status = MD_OK;
    
        if (tx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            gp_uart1_tx_address = tx_buf;
            g_uart1_tx_count = tx_num;
            STMK1 = 1U;    /* disable INTST1 interrupt */
            TXD1 = *gp_uart1_tx_address;
            gp_uart1_tx_address++;
            g_uart1_tx_count--;
            STMK1 = 0U;    /* enable INTST1 interrupt */
        }
    
        return (status);
    }
    
    

    一時的な割り込み禁止とそれの解除を行っております。必要に応じてそれらを行うことは本末転倒とかではありません。

    > 原因が特定できたのは大きな前進です。

    今回の指摘は不具合の可能性のひとつにすぎないのでこれを原因の全てと判断するのは早いでしょう。UART の送信バッファについて書き込みと読み出しのインデクスの両方が一致した場合はバッファが空という判断で両方を 0 に初期化するという処理をメーン処理と割り込み処理の両方で行う現状の実装には不安があります。メーン処理では書き込みのみ、割り込み処理では読み出しのみのインデクスをそれぞれ書き換えることゝし、バッファはリングバッファにする方が望ましいと思います。また、バッファの空に余裕がなく 0xff を格納する場面が度々あるのであればバッファの容量が足りないということなので現状の 300バイトよりは増やした方が良いでしょう。他、コード全体についても適切でないロジックが使われている可能性は考えられるので、コードレビュー等の手段で不安要素の洗い出しを行うことをお勧めします。