地雷を踏んだ
構造体へのポインタ配列の表示を確認していて、とんでもない地雷を踏んでしまいました。orz
MPLAB-Xの構造体へのポインタ配列の表示方法はこんな感じでした。
(1)展開前はtest_arrayがTest*[16]タイプでAddressが0x300という表示
(2)展開するとtest_array[0]からtest_array[15]まで縦に表示
(3)配列要素を展開するとTest構造体のメンバを表示
DWARFのデータ構造をそのまま辿っているようです。ふむ、これで良いのか・・・
ん!
異変に気付きました。test_array[0]の値が0?
test_array[0]の指し示す構造体メンバが0と7d?
そしてさらにおかしなことが・・・
test_arra[0]にtest0構造体のアドレスをセットして・・・あれ0まま!
test_arra[1]にtest1構造体のアドレスをセットしても・・・0まま!
さらにステップ実行すると・・・えっ!
ここに飛ぶ???
(暫く錯乱状態に陥ったのですが割愛します)
原因はこれ(↓)でした。
struct Test *test_array[16] @0x300;
ポインタ変数が1バイトのときと2バイトになるときの条件を調べようとして、test_arrayの配置を0x300に設定して、この異変に遭遇しました。
こんな所に変数を配置する奴(<==愚か者)がいけない
XC8って、煩く文句をつけるくせに、こういうドジは黙ってスルーするイケズです。
ぼちぼち、開発を進めて行く
長期に亘る作業計画(1か月とか3か月とか)は途中で100%頓挫することが分かっているので、最近は数日先くらいの作業予定しか立てません。(^_^;)
昨日(8/28)の作業予定は”ステータスの取得と表示”でした。
三種(RUN/BREAK/IDLE)のステータスをPIC側で設定して、ホストのメイン・ループで読み取り画面表示するというもので、大きなトラブルも無く作業を終えました。
<当面の開発予定>
2017.08.31 var other表示、ビット表示、integer表示
2017.08.30 Debugger-Dialog削除、Monitor-Dialog名称変更==>Select
Help表示方法検討
<本日> Loadとcinit、BreakとRESTART
一つ目は、”HexファイルをLoadしたら、RUNコマンドでcinitから起動できるようにする”というものです。(今はcinitを起動関数に指定する必要がある)
二つ目は、”自動BREAKが掛かったときにBREAKとRESTARTのメニュー切り替えを自動で行う”というものです。(今はBREAKメニューを操作する必要がある)
どちらも、手順ははっきりしているので、時間を掛けずに作業を終えることができると思います。(多分)
空いた時間を使って、未だ作業方針の立たない検討事項の調査を行います。
今一番の課題は・・・
『構造体へのポインター配列の表示方法の検討』です。
う~む、MPLAB-Xはどのように表示していたっけ?
先ずは、その確認から始めます。
なんとかLチカが動いた
『Lチカ・プログラムをLoadして動かす』
やろうとしたのはそれだけなのですが・・・
LoadコマンドでLチカ・プログラムをFlashメモリに書き込みました。
Runコマンドで所定のアドレス(cinit)から走り出しました。
ただ・・・
直ぐ、0番地付近に設定したBreakポイントに引っかかってしまいます。orz
Addressブレークは掛るのですが、後からLoadしたプログラムの実行をトレース出来ないので(STEP実行が効かない)、一体どこをどう走っているのか分からないまま、手探りのデバッグが始まりました。(ここからが長かった。)
何度も再起動しながら、様子を探っていきます。
Stack-OverFlow等でResetした訳では無いらしい、(PCONはクリアされたまま)
BREAK_STATUSの反復読み出しを止めると走り続ける、
モニタ・プログラムのTEST_FUNCならBREAK_STATUSの反復読み出しを止めなくても走り続ける、
・・・・・・・これって一体、何が起きてるんだ?・・・・・・・
モニタ・プログラムとLチカ・プログラムのリストを比べていて・・・ん!
COMMONのレジスタが、一部被っている?
Linkerコマンドでモニタ・プログラムとLチカ・プログラムのCOMMON領域を分けてみると・・・ビンゴ~
ようやく、Lチカが動きました。
『たかがLチカ、されどLチカ』
これは非常に大きな一歩になりました。
ーーーーーーーーーーーーーーーーーーーーー
補足(2017.08.28)
XC8コンパイラは、特別に管理された領域(Compiled Stack)に自動変数や一時変数を置きます。その際、プログラム全体のコール・グラフを作成して、変数の配置に重複が生じないようにしています。(XC8 マニュアル 5.5.2.2.1 Compiled Stack Operation参照)
Lチカ・プログラムとモニタ・プログラムは別々にコンパイルしたので、変数の配置に重複を生じていました。別にコンパイルしたプログラムをLoadして実行する、というのは今回の『肝』なので、そこは譲れません。そこでLチカ・プログラムとモニタ・プログラムでCompiled Stackが異なる領域に配置されるよう、リンカへ指示してトラブルを回避したと言う訳です。
以上、ご参考まで
課題が一つ解決した
今手掛けているProjectの目指す所は『PICの学習環境であり、プログラムの開発環境であり、アプリケーションの実行環境』です。<==大風呂敷 (^_^;)
『PICの学習環境』として提供するのは、レジスタやメモリをREAD/WRITEしたり、小さなテスト・プログラムを走らせてその結果をモニタする機能です。
『プログラムを1行も書かずに、TRISAレジスタとLATAレジスタをコンソール画面から操作するだけで、Lチカが出来る』というのが一番の売りです。
『プログラムの開発環境』として提供するのは、関数を呼び出して処理結果を確認したり、プログラムの実行にBreakを掛けたりする機能です。プログラム実行中にもGlobal変数をモニタしたり、書き換えたり出来るのが自慢です。
(PICkit3では出来ません。<==やらないだけ?)
最後の『アプリケーションの実行環境』として、PIC側でユーザ・プログラムのcinitを呼び出す機能の提供は当然のこととして、ホスト側のユーザ・アプリケーションをどうやって起動するか?その方針が決まっていませんでした。
構想としては、ユーザが作成したjarファイルをライブラリ登録して、コンソール・プログラムからそれを呼び出すというものなのですが、果たして上手くいくものか?
試してみました・・・
中央水色がユーザが定義したウィンドウで、User_Frame.jarをライブラリ・フォルダに配置して、呼び出すことが出来ました。ここまでならライブラリを組み込んでそれを呼び出しただけなので、出来て当然です。
気になっていたのは、コンソール・プログラムをコンパイルした時と異なるUser_Frame.jarでも問題なく呼び出せるのか?という点です。パネルを表示するだけのUser_Frame.jarでコンパイルした後、1秒ごとにボタンを貼り替える動作を組み込んだUser_Frame.jarに置き換えても、問題なく動きました。
どうやら、コンソール・プログラムとのインターフェース部分に変更がなければ、ユーザ・アプリケーションをUser_Fram.jarとして呼び出すことが出来そうです。
ホスト側のユーザ・アプリケーションの起動方針が固まりました。
(パチパチパチ~)
放置していた不具合に対処した
通信ポートは、現在の環境に合わせてWindowsなら”COM4”、Linux(Raspi)なら”/dev/ttyUSB0”を設定しています。これを利用者の環境に合わせて設定できるようにする計画なんですが・・・
何らかの事情でPICと通信できない場合、今までのプログラムは受信待ちループから抜けられませんでした。ユーザが利用する通信ポートを設定しようとしても、メニュー操作では設定出来なかったのです。(起動パラメータで指定するという方法はあります)
そこで、先ずは受信待ちの無限ループにエラー終了する機能を追加しました。
<以前のコード>
if( receive_ringbuff.rest() < len )return false;
<書き換えたコード>
if( receive_ringbuff.rest() < len )
{
receive_loop_count++;
if( receive_loop_count < 100 )return false;
Main_Frame.main_control.target_pane_panel.show_message("Receive fail");
}
100ms待っても必要なデータが受信できなければ、"Receive fail"というメッセージを表示して抜けるようにしました。(送信は通信不良でもプログラムは正常終了するのでこの処置は入れていません)
さらに
try{
CommPortIdentifier comID = CommPortIdentifier.getPortIdentifier( port_name );
---一部省略-----
port_open = true;
}
catch( Exception e )
{
Main_Frame.main_control.target_pane_panel.show_message("Comm port fail");
port_open = false;
}
通信ポートが開けなかった場合に、"Comm port fail"というメッセージを表示して
port_openフラグをfalseにしました。
port_openフラグがfalseのときは通信処理をスキップして、メニュー操作が出来るようになりました。
目出度し、目出度し
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
後の方の処置だけだと、シリアル・ラインの断線に対処できません。実は前の方の処置だけで動くのですが・・・
何というか、まぁ、気分の問題ですね。
以上、ご参考まで
readelfの問題が解決した
Raspi上の動作確認で、関数リストを作成できないという不具合が発覚しました。
実は、WindowsとRaspiでreadelfの出力に違いがあることは分かっていて、解析内容は同じなのですが、Raspiの方はコメントが日本語になっています。
<Windows上のxc32-readelfの出力>
ーーーーーーーーーーーーーーーーーーーーーーーーーー
2017-08-22T14:31:25.412
Contents of the .debug_abbrev section:
Number TAG (0x0)
1 DW_TAG_compile_unit [has children]
DW_AT_stmt_list DW_FORM_data4
DW_AT_producer DW_FORM_string
<Raspi上のreadelfの出力>
ーーーーーーーーーーーーーーーーーーーーーーーーーー
2017-08-22T05:43:36.536
.debug_abbrev セクションの内容:
Number TAG (0x0)
1 DW_TAG_compile_unit [子あり]
DW_AT_stmt_list DW_FORM_data4
DW_AT_producer DW_FORM_string
ロケールをUKにするとコメントが英語になり、関数リストも期待通りに作成されました。メカニズムは分かっていませんが日本語のコメントが関数名の解析を妨げているようです。
対策として、Raspiの場合は”env LANG=en_us.UTF-8 ”を頭につけてreadelfを呼び出すことにしました。
こんな感じです
ーーーーーーーーーーーーーーーーーーーー
if( !isLinux() ){
analyze_elf = readelf;
}
else{
analyze_elf = "env LANG=en_US.UTF-8 "+readelf;
}
OSの種類をこんな風に取得し、
String OS_NAME = System.getProperty("os.name").toLowerCase();
判定はこんな感じです
boolean isLinux() {
return OS_NAME.startsWith("linux");
}
この修正によりRaspiでも関数を指定できるようになりました。
(パチパチパチ~)
Raspi上で動かしてみた
当初はRaspiとPIC間の通信にI2Cを使う計画でした。しかし、USBシリアルを利用すればPCでも同じアプリが動くことに気付き、UART通信でやることにしました。
それ以来、開発はPC上で進めてきましたが、粗方動き始めたので久しぶりにRaspi上で動かしてみることにしました。PCで動いたアプリがそのままRaspiでも動くというのがJAVAの有難いところです。
とは言ってもNetBeansが全てやってくれるPCとは違い、jarファイルを配置したり、classpathを通したり、Raspi上で動きだすまでにはひとしきりジタバタさせられましたが、漸く動きました。(パチパチパチ~)
殆どPCと同じように動くのですが、何故か関数リストが作成されていません。orz
まだまだ、完成にはほど遠いようです。