2012/04/15

[arm]カレントプログラムステータスレジスタ(CPSR)

LPC2388のサンプルソースを見ていると、やたらとインラインアセンブラが出てくる。
そのほとんどが、CPSRって書いてある。
何だろう?

Current Program Status Registerの略で、レジスタだからCから直接触れない。
ARM用のコンパイラとかだったらわからないけど、gccにはできん。
なので、インラインアセンブラで直接触らんといかん。
まあ、専用コンパイラじゃないから仕方ないか。

これの下位5bitが、モードビットと呼ばれている。
ARMにはいくつか動作モードがあって、そのうちのどれで動作しているかがわかるし、設定できるようだ。
この資料が、LPC2388のドキュメントにはなさそうだ。
ARMのレジスタなので、ARMのドキュメントから探さねばならん。
ということに、なかなか気付かなかった。
#    CPRSのモードビット(M[4:0])
#        1_0000(0x10) : ユーザ
#        1_0001(0x11) : FIQ
#        1_0010(0x12) : IRQ
#        1_0011(0x13) : スーパバイザ
#        1_0111(0x17) : アボート
#        1_1011(0x1b) : 未定義
#        1_1111(0x1f) : システム


なんでこんなのを調べているかというと、単にFIQを使いたいからだ。
Interface誌のサンプルでは、startup.sの中にあるアセンブラが呼ばれて、そこからCソースが呼ばれて、戻ってきてから復帰処理、という流れになっている。
が、まだるっこしい。
Cソースで関数が割り込みハンドラになっていれば、直接ジャンプしてもいけるはず。
探すと、Cソースでこんな感じにしておけばFIQハンドラとみなされるようだ。
void isr_FIQ(void) __attribute__ ((interrupt("FIQ")));
ARMはベクタテーブルは固定のようで、ここにはジャンプ先が書いてあるのではなく、ジャンプした後の処理を書くようだ。
「LDR PC, 処理先アドレス」のようになっている。
ここの処理先アドレスにさっきのCソースの関数を書けるかというと、そうはなってない。
一度別の場所に飛び、そこからCソースの関数へジャンプしている。
たぶん、ベクタテーブルが4byteごとなので、ここには4byte分の処理しか書けない。
ジャンプ命令は32bitアドレスどこへでもいけるようなので、4byte以上の命令長になるから一端中継する必要があるのだろう。
ただ、Interface誌のサンプルでは中継点で割り込みからの復帰処理を書く必要があったのだけど、__attribute__を使うとそこまでコンパイラがやってくれるようなので、ジャンプ命令だけで済んでいる。

あと、ARMの動作モードごとにスタックポインタ(R13)があるので、必要なモードだけスタックを設定しなくてはならんようだ。
これが、
  1. CPSRのモードビットに書き込んでモード遷移する
  2. R13にスタックポインタをLDRで設定する
ということをやるようなのだ。
いやあ、めんどくさい。
一度作ってしまえばいいんだろうけど、初回はめんどくさいな。
(まねするだけだけど。)

サンプルでは、スタックポインタをmemory.defの中で決めている。
0x4000_F000~0x4000_FFFFまで取っている。
4KBか・・・今使っているマイコンの全メモリと同じじゃないか・・・。



サンプルソースでは、IRQを使っているようだったが、私はFIQを使ってみたい。
IRQに比べて何が「F」なのかというと、専用のレジスタが多いためのようだ。
ARMの汎用レジスタは、
  • R0~R15
  • CPSR
  • (SPSR_xxx)
らしい。
IRQモードになると、R13、R14、SPSR_irqだけがIRQ用になり、それ以外は共通。
FIQモードでは、R8~R14とSPSR_fiqがFIQ用になる。
違いはR8~R12で、レジスタをいろいろ使い分ける必要があるなら、IRQでは一度現在のレジスタ値をどこかに退避させ、処理が終わったら戻してやらないといかんのだろう。
FIQは専用なので、そこら辺を気にしなくていいのが「F」ってことのようだ。

では、FIQを発生させるにはどう変更すれば良いだろうか。
ブロック図を見ると、IRQとFIQはVIC(Vector Interrupt Controller)から入力される。
サンプルはタイマ割り込みを発生させているので、そのままだとVICがIRQを出すのだろう。
割り込みの選択は、Interrupt Select Register(VICIntSelect)で行われている。
割り込み要因ごとに、IRQにするかFIQにするかが選択できる。
デフォルトは、全部IRQ。
割り込み要因を有効にするかどうかは、Interrupt Enable Register(VICIntEnable)で行う。
デフォルトは、全部割り込み無効。
そして、IRQやFIQ自体の有効/無効は、ARM側のレジスタになる。
これまたCPSRだ。
割り込み禁止/許可の専用命令はないらしい。
VICはメモリにマップされているのでCソースからも扱いやすいのだが、CPSRはアセンブラじゃないといかん。
頻繁に使うので、こちらをそのまま採用したマクロにした。
R12ってレジスタをハードコーディングしているけど、いいのかな?
コンパイラがうまいことやってくれるような風に書かれているけど、「In general」ってあるよなあ。
clobber listってのに書いておくからよい、という解釈でいいのかな。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。