2012/07/21

[os]昨日の動かないところを調べよう

昨日は眠たすぎて、よくわからないことを書いていた。
整理しよう。

TOPPERS/ASP + FM3で、シリアルポートをオープンすると、Cortex-M3のHardFault例外が発生する。
箇所は、x_unlock_cpu()したときのset_basepri()。

image

上の図で、msrするところは進んだけど、次のaddでステップ実行すると例外が発生。
ちなみにOpenOCDのtelnet側でpollすると、こんな感じになる。

image

HardFaultってのがわかるんですな。


set_basepri()だが、こんな実装になっている。

image

MSRという命令は、普通のレジスタの値から特殊レジスタへ書き込みを行う。
ARMv7-Mでは、BASEPRIも指定できるので、問題ない。
うーむ。

上のアセンブラを見るとわかるが、これは関数呼び出しされている。
-O0でコンパイルしているからかも。
なんとなくだが、これはインライン展開されないとよろしくないようになっているのでは?


そうかどうかはわからないので、BASEPRIを見てみよう。

2.1.3. Core registers

Base Priority Mask Registerらしい。
Interface誌にも書いてあった。割込のマスク制御関係らしい。
じゃあ、これ自体は呼び出してもあんまり関係ないか。

 

「Privileged」と書いてあるので、特権モードじゃないと使えないのだろう。
Cortex-M3は、どういう動作モードがあるのだろうか?

3.2.1. 動作モード

おや、「スレッドモード」と「ハンドラモード」の2つしかないんだ。
OpenOCDで「current mode」に出てくるあれは、これだったんだ。
pollって打つとこれが見られるので、msr実行時に見てみればよいか。

image

スレッドモードやん。。。
いや、よく読め。
スレッドモード・ハンドラモードは「動作モード」で、これとは別に「特権アクセス」がある。
スレッドモードでも、特権アクセスは可能なのだ。

CONTROL[0]をクリアするとユーザモードらしいので、CONTROLレジスタというものを見ればよいのかな。

・・・Eclipseに出てくるレジスタ一覧に出てこない。
そういえば、BASEPRIもレジスタなのだけど、出てこないな。

telnet側から「reg」と打つと、出てくるらしい。

image

CONTROL[0]は、0。
よって、ユーザモード。

つまり、スレッドモードかつユーザモードで動いている、と。
なして?


controlでソースを検索すると、sense_context()で使われている。
unl_cpu()の頭にあるCHECK_TSKCTX()で呼んでいる。
ここでは、0x02が返ってきているので、regで見た値と同じだ。

sense_context()は、CONTROLレジスタの値と0x02をANDして、0x02ならばfalse、それ以外ならtrueを返すようになっている。
CONTROLレジスタのbit1は、スレッドモードでスタックとしてメインスタックとプロセススタックのどっちを使うかを決めているところのようだ。
ASPにはCONTROL_PSP(0x02)とCONTROL_MSP(0x00)があるので、PSPがプロセススタックポインタ、MSPがメインスタックポインタを意味しているのだろう。
こちらがわかりやすい。

sense_context()がfalseを返し、falseはPSPで、PSPはスレッドモードでしか使用できない。
これが意味するところは何だろうか?
sense_context()はCHECK_TSKCTX()で呼んでいると書いたが、マクロでこうなっている。

   1: /*
   2:  *  呼出しコンテキストのチェック(E_CTX)
   3:  */
   4: #define CHECK_TSKCTX() {                                    \
   5:     if (sense_context()) {                                    \
   6:         ercd = E_CTX;                                        \
   7:         goto error_exit;                                    \
   8:     }                                                        \
   9: }

trueだと、エラーらしい。
だから、PSPであるのが正しい、という流れだ。つまり、スレッドモードで正しいのよ。
(PSPのときは「タスクコンテキスト」と呼んでいる)

なんだなんだ?


落ち着け。
だいたいこういうときは、自分が何かミスをしているのだ。

まず、現象が起きているのは、CPUロック状態の解除を行う、x_unlock_cpu()の中で呼んでいるset_basepri()。
CPUロック状態を解除するということは、その前にロックを行っているということだ。
それもset_basepri()を呼んでいるはずで、そこでは失敗していない。
よし、そこを確認しよう。

 

ロックは、x_lock_cpu()。
流れはunlockと同じで、コンテキストがタスクコンテキストであることを確認し、set_basepri()している。
書き込む値は、0x10。
pollを見て、まだスレッドモードにいることと、regを見てユーザコンテキストであることを確認。
さて・・・問題なし。

 

ということは、msrがどうのこうのではなくて、CPUロックを解除したことによって何か割込が発生して、そっちが原因ということか。

そうだよな、msr命令で例外が発生するなら、実行してすぐに発生するはずだけど、1ステップ後だったからな。


アボートモデルによると、HardFaultが発生する要因は4つあると。
どれも凶悪だ。
さて、なんだろうね。

シリアルポートをオープンした直後だから、シリアルの割込関係か。
そういえば・・・cfgファイルをかなり適当に設定してたよな。

   1: INCLUDE("target_timer.cfg");
   2: //INCLUDE("syssvc/syslog.cfg");
   3: INCLUDE("syssvc/banner.cfg");
   4: INCLUDE("syssvc/serial.cfg");
   5: //INCLUDE("syssvc/logtask.cfg");
   6:  
   7: #include "snep_target.h"
   8:  
   9: CRE_TSK(LED_TASK, { TA_ACT, 0, main_task, 7, 128, NULL });
  10:  
  11: CFG_INT(IRQ_VECTOR_MFS4RX, { TA_NULL, INTPRI_SIO });
  12: ATT_ISR( { TA_NULL, INTATR_SIO, IRQ_VECTOR_MFS4RX, NULL, 1} );
  13: CFG_INT(IRQ_VECTOR_MFS4TX, { TA_NULL, INTPRI_SIO });
  14: ATT_ISR( { TA_NULL, INTATR_SIO, IRQ_VECTOR_MFS4TX, NULL, 1} );

よくよく読むと、INCLUDEしているsyssvc/serial.cfgの中でtarget_serial.cfgをINCLUDEしていた。
target_serial.cfgは、各ターゲットごとのファイルで、cq_frk_fm3_gccではこうなっていた。

   1: /*
   2:  *  SIOドライバ(cq_frk_fm3用)のコンフィギュレーションファイル
   3:  */
   4:  
   5: #include "target_serial.h"
   6: ATT_INI({ TA_NULL, 0, sio_initialize });
   7: ATT_ISR({ TA_NULL, SIO_PORTID, INTNO_SIO_RX, sio_rx_isr, 1 });
   8: ATT_ISR({ TA_NULL, SIO_PORTID, INTNO_SIO_TX, sio_tx_isr, 1 });
   9: CFG_INT(INTNO_SIO_RX, { TA_ENAINT|INTATR_SIO, INTPRI_SIO });
  10: CFG_INT(INTNO_SIO_TX, { TA_ENAINT|INTATR_SIO, INTPRI_SIO });

 

私はRC-S620/Sを1番につなぎたかったので、target_config.hでSIO_PORTIDを2に変更している。
そうすると、INTNO_SIO_RXなどはtarget_serial.hの中でIRQ_VECTOR_MFS0RXなどに変更される。
私はMFS4RXなどを使いたかったので自分のcfgで定義したんだけど、これだと割込を使うくせにハンドラが登録されていないってことになるのかな。

とりあえず、はずしてみよう。

   1: INCLUDE("target_timer.cfg");
   2: //INCLUDE("syssvc/syslog.cfg");
   3: INCLUDE("syssvc/banner.cfg");
   4: INCLUDE("syssvc/serial.cfg");
   5: //INCLUDE("syssvc/logtask.cfg");
   6:  
   7: #include "snep_target.h"
   8:  
   9: CRE_TSK(LED_TASK, { TA_ACT, 0, main_task, 7, 128, NULL });

 

・・・例外が発生しなくなった。
前も同じにして動いていたのだけど、あれはRXDをRC-S620/Sに挿してなかったからなのか?
いやいや、前回そうしたのは、セマフォIDが入らなかったからだ
なぜだ、なぜなんだ・・・・・・・。

今日は時間がないから、深追いはやめよう。
だが、送信でまた失敗するので、追うことになりそうだ。

0 件のコメント:

コメントを投稿

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

注: コメントを投稿できるのは、このブログのメンバーだけです。