ちょっと長くなりますが、悩んでいますので、ご存じの方がおられたらよろしくお願いします。内容ですが、以下のようなことです。
ある割り込み処理ルーチンAから、別の割り込み処理ルーチンBを呼ぶようなコードが書かれていました。おそらくこのようなコードでは、PSWが破壊されると思いますが、割り込みがかかったメイン処理ルーチンmainになぜに影響があるのかが理解できません。でも、実際に、AからBを呼ばないようにすると、安定して動作するのです。以下はその現象のサンプルです。
#pragma interrupt A_interrupt(vect=XX1)#pragma interrupt B_interrupt(vect=XX2)
static void A_interrupt(void){ B_interrupt();}
static void B_Interrupt(void){ // 処理 // ..}
static unsigned char ip=0;static unsigned char op=0;
void main(){ while(1){ if( ip != op ) { sub_func(); } }}
条件は以下となります。Aの割り込みは頻繁にかかります。Bの割り込みは決してかかりません。
上記処理では、ip,opが変化がないとすると、main()では決してsub_func()は呼ばれないはずですが、Aの割り込みがかかるとなぜかsub_func()が呼ばれるようになります。そして、Aの割り込み処理からBの割り込み処理を呼ばないようにするとsub_func()は呼ばれないようになります。
動作の推察:1.Aの割り込みでは、スタックに PSW(1バイト)、PC(3バイト)が保存されます。2.AからBを呼ぶと、スタックに、0値(1バイト)、PC(3バイト)が保存されます。3.B処理から戻るときRETIが実行され、PSWには0が取り込まれて、コールした時とは異なる値でPSWが破壊されます。4.Aの処理終わりで、再びRETIが実行され、スタック上に保存されたPSWが復帰されます。
以上の状態となると、main()でおかしな動作にはならないと思われるのです。ちゃんと、Aの割り込みの終わりで、PSWが戻っているので。でも、実際にはPSWが破壊されて、おかしな動作になっていると思われるのです。
どうして、上記のような処理でおかしくなるのか、どなたかご存じでしょうか。
実を言えば、Aから割り込み処理Bを呼んだ事が不具合の原因だと思われ、呼ばないようにすると改善することはテストで確認しました。
しかし、CPUの動作仕様からすると、不具合も起こらないはずにしか思えません。これでは、本当に不具合が修正できているのかわからないのです。
割り込みハンドラ(割り込みベクタ)からの復帰と通常の関数(サブルーチン)からの復帰はCPU内部で動作が違うのでコンパイルされた関数からの戻りとして使われる機械語が違います。RL78ならRETIとRETがあり、割り込みハンドラではRETIが戻りとして使われるので割り込みハンドラを直接関数のように呼び出すと異常な状態になります。もし、ある割り込みハンドラの内部処理を他の割り込みや通常の処理から呼び出したいならその処理を関数として作り直して呼び出すようにすれば解決します…
チョコです。
>それに、この機能は未初期化のメモリアクセスをチェックするものではなくて、RAMのハード的なビット化けのチェックらしいので、今回の件には当てはまらないでしょう。
RL78/F13のハードウェア マニュアルをECCで検索してみたのですが、DTCの所に以下のような記述がありました。ECCは通常は、データ領域に対して使うものですが、どこにもアドレスの記述はありません。DTCでの読み出しで割り込みが発生するなら…
ちょっと気になったので、実際にどの様に動くかやってみました。
簡単な割り込み関数を定義して動作を見る限り、mus.さんの仰っている通り、メイン関数に戻るところでスタックに書かれている値をPSWに戻すので、メイン関数の動作がおかしくなるという現象は見られませんでした。
そこで、どのようなケースでおかしくなるか。意図的に動作がおかしくなるなケースを作ってみました。
#pragma interrupt…
すいません。チップは、RL78/F3です。
割り込みハンドラ(割り込みベクタ)からの復帰と通常の関数(サブルーチン)からの復帰はCPU内部で動作が違うのでコンパイルされた関数からの戻りとして使われる機械語が違います。RL78ならRETIとRETがあり、割り込みハンドラではRETIが戻りとして使われるので割り込みハンドラを直接関数のように呼び出すと異常な状態になります。もし、ある割り込みハンドラの内部処理を他の割り込みや通常の処理から呼び出したいならその処理を関数として作り直して呼び出すようにすれば解決します。www.renesas.com/.../78k0-series-instructions
Yamamoto様ご回答ありがとうございます。
リンクを頂いている78K/0シリーズと、今回使用しているRL78/F13とは仕様が異なるようです。リンクされている78K/0シリーズでは、スタックをCALLで2バイト、割り込みでは3バイト消費し、RETでは2バイト戻し、RETIでは3バイト戻しますので、SPがずれてしまい明らかにおかしな動作になるでしょう。しかし、RL78/F13では、PCは20ビットあり、CALLでも、割り込みでもSPは4バイト消費します。つまりSPに関してはずれが無い仕様なのです。
したがって、今回の問い合わせの場合、そのようなコードを書いても不具合にならないように見えるのが問題なのです。つまり、
1.割り込み処理ルーチンから割り込み処理ルーチンをコールしたことで不具合が発生するのか?2.発生するとしたらどのような仕組みから起こっているのか。3.Bを割り込み処理ルーチンから通常のサブルーチンにしたら改善することが証明できるか?
ということが知りたいのです。
わわいです
それが知りたいのなら、デバッガで、割り込み関数の入り口でブレークポイントを設定し、逆アセンブリレベルでワンステップづつ実行させてみましょう。
そうやってレジスタの内容、コードの挙動などを追いかけていけば何が起こるのか、がわかると思いますよ
78K/0しかオペコード一覧がわかるのが見つからなくて、わかりづらくてすみません。割り込み時はPSWの待避動作があります。RETIとRETBは呼ばれるとPSWをスタックから値を取り出してPSWにセットします。RETはそういう動作をしません。
これは、懐かしい「RAM パリティ・エラー検出」によるリセットがかかっているものと考えられます。
CALL命令では、PSWをスタックには書かれていないので、RAMの値は不定、それをRETIで読み出したものだから、たまたまパリティのエラーが発生したてたものでしょう。そのため、リセットがかかり、動作がおかしくなったものです。
この状態はデバッガでも確認は難しいですね。
以上
かわい様
返信ありがとうございます。
使用している開発環境のe2Studioでは、逆アセンブル表示はできることがありますが、
そのアセンブラリスト上でのステップ実行はできないようです。
なにか方法があるんでしょうか?
チョコ様
RL78/F13には、RAMのパリティチェック機能はないようです。ハードウェアマニュアルには記載がありませんでした。
ただし、ECC機能はあるようですが、ECCチェックエラーで割り込みを発生できるのは
デバック中でないときのみのようです。現在は開発中なので、常時デバッカーをつないで動作させています。
それに、この機能は未初期化のメモリアクセスをチェックするものではなくて、RAMのハード的なビット化けのチェックらしいので、今回の件には当てはまらないでしょう。
逆アセンブリリスト上でもブレークポイントを設定できますんで、それでいろいろやってみてください
RL78/F13のハードウェア マニュアルをECCで検索してみたのですが、DTCの所に以下のような記述がありました。ECCは通常は、データ領域に対して使うものですが、どこにもアドレスの記述はありません。DTCでの読み出しで割り込みが発生するなら、RETIでの読み出しでも同様ではないかと考えられます。
ECCの所には、以下のような記述もあります。
割り込みの設定をどうしているかは、分かりませんが、スタック領域は初期化しておいた方がいいのではないかいと思います。確かスタートアップにスタック領域の初期化サブルーチンがあり、そこを呼ぶところがコメントアウトされていたような記憶があります。