SHで、処理中の割込み禁止、許可処理をどう実現すれば良いか迷っています。
SuperH C/C++コンパイラパッケージ アプリケーションノート(RJJ05B0557-0700)には以下のコードが記載されています。
#define disable() { save_cr=get_cr(); set_imask(0x0f); }#define enable() { set_cr(save_cr); }
この処理で疑問なのが、なぜステータスレジスタをまるごと退避、戻しを
しているのか?という点です。
禁止時はget_imaskでIビットの値を退避、許可時はset_imaskで
退避したIビットの値をIビットに戻せば良い気がするのですが。。。
みなさまはどのような処理で割込み禁止/許可を行っていますでしょうか?
> この処理で疑問なのが、なぜステータスレジスタをまるごと退避、戻しをしているのか?という点です。
disable() 直前の割り込み許可状態を enable() で復元したいんじゃないですか。save_cr が大域変数だと上手く動作しないとは思いますが。
割り込みが許可、あるいは禁止状態で行う処理や関数で一時的に割り込みを禁止して後に割り込み許可状態を禁止直前の状態に復元したい、ということは普通にあります。
実際にはdisableが実行する前に、元のSRの内容はスタックに保存されているんですよね。
また割り込みマスクを0xfに設定する前に、より優先度の高い割り込みが入る事を防ぐ事ができなかったりする。
fujita nozomu さん、@chobichan さんお返事ありがとうございます。
すいません、質問の仕方を変えます。
SHでの割込を一時禁止するやり方ですが、今は、
SuperH C/C++コンパイラパッケージ アプリケーションノート
(RJJ05B0557-0700)の以下のコード(マクロ)
#define disable() { save_cr=get_cr(); set_imask(0x0f); }
#define enable() { set_cr(save_cr); }
を参考にして、以下のようなコードを書いています。
int save_cr;
// 割込み禁止処理
save_cr=get_cr();
set_imask(0x0f);
// 割込み禁止にした状態で行いたい処理
test_flg = 1;
// 割込み許可処理
set_cr(save_cr);
上記の方法で良いのでしょうか?
get_cr()関数ではなく、get_imask関数を使って
以下のやり方でも良いのでしょうか?
int save_i;
save_i = get_imask();
set_imask(save_i); ←15/07/21 誤って「set_cr」と、していたので直しました。
割込み禁止区間を設ける場合、私は「get_imaskとset_imask」を用いた方を使います。
SRレジスタを退避しても出来そうですよね。割込みレベルのマスクビットってSRレジスタの中にある4ビットですから。
未確認ですが、機械語レベルでみると「get_cr()とset_cr()」を用いたほうが命令数が少いかもしれません。
※SRレジスタ操作なのにcrというのが気に入らないんですよね。
割り込み禁止/解除の方法はどっちでもいいと思うのですが、get_cr()/set_cr() のほうが get_imask()/set_imask() よりコードは短く実行も速いので有利だと思います。
void test_cr(void) 00001020 7FFC _test_cr ADD #H'FC,R15 { int save_cr; // 割込み禁止処理 save_cr=get_cr(); 00001022 0402 STC SR,R4 00001024 2F42 MOV.L R4,@R15 set_imask(0x0f); 00001026 0002 STC SR,R0 00001028 9122 MOV.W @(H'0044:8,PC),R1 0000102A 2019 AND R1,R0 0000102C CBF0 OR #H'F0,R0 0000102E 400E LDC R0,SR 00001030 D510 MOV.L @(H'0040:8,PC),R5 // 割込み禁止にした状態で行いたい処理 test_flg = 1; 00001032 E201 MOV #H'01,R2 00001034 2522 MOV.L R2,@R5 // 割込み許可処理 set_cr(save_cr); 00001036 440E LDC R4,SR } 00001038 000B RTS 0000103A 7F04 ADD #H'04,R15
void test_imask(void) { int save_i; // 割込み禁止処理 save_i = get_imask(); 0000103C 0002 _test_im STC SR,R0 0000103E 4009 SHLR2 R0 00001040 4009 SHLR2 R0 00001042 C90F AND #H'0F,R0 00001044 6603 MOV R0,R6 set_imask(0x0f); 00001046 0002 STC SR,R0 00001048 9512 MOV.W @(H'0024:8,PC),R5 0000104A 2059 AND R5,R0 0000104C CBF0 OR #H'F0,R0 0000104E 400E LDC R0,SR // 割込み禁止にした状態で行いたい処理 test_flg = 1; 00001052 E101 MOV #H'01,R1 00001054 4608 SHLL2 R6 00001056 2412 MOV.L R1,@R4 00001058 4608 SHLL2 R6 0000105A 0702 STC SR,R7 0000105C 2759 AND R5,R7 0000105E 276B OR R6,R7 00001060 470E LDC R7,SR // 割込み許可処理 set_imask(save_i); 00001050 D408 MOV.L @(H'0020:8,PC),R4 00001052 E101 MOV #H'01,R1 00001054 4608 SHLL2 R6 00001056 2412 MOV.L R1,@R4 00001058 4608 SHLL2 R6 0000105A 0702 STC SR,R7 0000105C 2759 AND R5,R7 0000105E 276B OR R6,R7 00001060 470E LDC R7,SR } 00001062 000B RTS 00001064 0009 NOP
皆さんとは考えが逆行しますが、安全側に倒れることを想定するなら、set_imask()/get_imask()のほうがいいと思います。
割り込み許可までにメモリ破壊が発生したら目も当てられません。
Kon Nozomu(すと)さん、
メモリ破壊とはどこのことを指してのことでしょうか? メモリの内容が破壊されても安全に動作するシステムはまた別の課題だと思いますが。
退避内容が破壊されたときを考えるとリスクの少ない方がいいと思いましたが、
メモリが(スタック、大域変数)壊れているんだったら何をやっても無駄、とも言えますね。
私だったらコード効率よりも、よりリスクの少ない方(必要最低限の情報へのアクセス)を選択しますという意味でした。
fujitaさんがご指摘されるようにメモリ保護は別の課題ですね。
fujitaさん
結構、差がありますね。
私も今後はget_crとset_crの組み合わせに乗り換えようと思います。
すとさん
メモリ破壊ですがset_imask()/get_imask()でも値はメモリにあるわけで破壊されて動作レベル値が変わったら、割込み禁止区間から抜けた時に動作レベルは引き継げないです。ただ、割込み禁止区間中のSRレジスタの割込みマスクビット以外は継続されるということが大事だという意味でしたらset_imask()/get_imask()が有効かもしれません。
私はC言語で記述しているだけでSRレジスタの中身まで見ないので「get_cr()/set_cr()」で良いと思います。「set_imask()/get_imask()」を使っていたのは すとさんが参照されているドキュメントを私も大昔に読んで割込みマスク設定・参照と明記されていためです。私が組込を仕事とした当初なのでget_cr()/set_cr()の組み合わせでも出来るかの判断が出来なかった。。。。判断できるようになった分、オッサンになったんだな。
みなさまコメントありがとうございました。
お礼が遅くなり申し訳ありませんでした。
コードサイズが小さいという点から
私もget_cr/set_crを使って行こうと思います。