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では上記のような関数はリエントラントにはならないのでしょうか?
チョコです。
アセンブラでの動作についてコメントさせてもらいます。
2F25 AEF8 8600 movw ax, sp
2F27 041500 8601 addw ax, #0x0015
でaxはスタック領域のスタックトップから21(0x15)バイト目を指しています。
axがディスティネーション側(転送先)なら,もろにスタック領域を破壊します。
(axが転送元で,スタックに積まれているデータを転送するならわからないではないですが。)
以上
> 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 への送信バッファ
という想定で考えると、
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[] の領域は溢れて宜しくないことになりそうな気はします。
Montx() が呼ばれる度に
> 実際の 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[] の領域は溢れることになると思います。
> 上記の割り込み処理で書き込む動作となります。
失礼致しました。仰る通りです。見落としておりました。
> が実行されるのみでこの関数は実行を終えるので、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 関連の割り込み処理が止められた場合、
が成立しない判定を行った直後のタイミングで割り込み処理により MTx[] に格納された最後の 1バイトを UART へ送信し、MTx[] の内容が空となり、UART 関連の割り込み処理が止められた場合、
タイミングを限定した条件での話をしてますよ。
> Montx関数内を割り込み禁止にするのは本末転倒な気がします。
コード生成されたコードでデータ送信をセットする関数 R_UART1_Send() の中で
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バイトよりは増やした方が良いでしょう。他、コード全体についても適切でないロジックが使われている可能性は考えられるので、コードレビュー等の手段で不安要素の洗い出しを行うことをお勧めします。