令和3年 6月
PIC32MMでのMPLABX シミュレータのバグ
これまでもPIC32MMにおけるMPLABXのシミュレータは実用に耐えないと書いてきました。
本来シミュレータは実機で動作確認をする前の段階での基本的な動作検証が目的です。
ところが実機では動作するプログラムがシミュレータ上では動作しない事が何度も起きるため、実用に耐えないという結論を出した。
幸いにもPIC32MXのシミュレーションは非常に良く動作してくれます。このため、アルゴリズムの検証などはPIC32MXでシミュレーションを行ない、PIC32MMでは実機での動作検証のみを行う形でプログラム作成を行ってきました。
ただ部分的にはPIC32MMでのシミュレーションを試す機会があり、その副産物としてシミュレータのバグ原因を見つけたので記録しておきます。
なお本内容はマイクロチップ社にも報告してあります。シミュレータが改善されることを期待します。
1.不具合現象を特定する
最初に不具合が発生する単純なコードを作る必要があります。過去の不具合現象から実験的に書いたコードが下記になります。
uint32_t a_array[12]; uint32_t b_array[12]; int main( void ) { initUart(); //UART初期化 print_out(); //UARTに初期値を出力 b_array[7] = 1; b_array[8] = 2; b_array[9] = 3; print_out(); //UARTに結果を出力 Nop(); for(;;){ Nop(); } return 0; }
このコードを実際のデバイスとシミュレータで動作させた結果は下記になります。最初の2行は変数の初期値で次の2行が変更後の値です。
実チップでの出力 a_array:0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b_array:0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a_array:0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b_array:0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, シミュレータでの出力 a_array:0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b_array:0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a_array:0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, b_array:0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
b_array[8]以降に書き込んだデータがなぜかアドレスが小さいa_array側のアドレスに出力されています。これは明らかにおかしな動作です。
2.デバッグのための情報を出力させる
このままではCPUが何をしているのか分からないので、リストファイルを出力してアセンブリコードを確認します。
リストファイルの出力方法はProject Propertiesを開いてCategoriesから該当するConfigrationのXC32(Global Option)を選択します。画面の右側に表れるAdditional option欄に'-Wa,-ah="<name>.lst"'を追加します。<name>は適当なファイル名です。''内に余分はスペースがあってはいけません。
下記はリストファイルの抜粋です。b_array[7] = 1;以降の動作を示しています。
85:z:/mplab/freertos/src/ztst/cpp_test/main.cpp **** b_array[7] = 1; 309 .loc 1 85 0 310 0138 A2410000 lui $2,%hi(b_array) 311 013c 42300000 addiu $2,$2,%lo(b_array) 312 0140 81ED li $3,1 # 0x1 313 0142 A7E9 sw $3,28($2) 86:z:/mplab/freertos/src/ztst/cpp_test/main.cpp **** b_array[8] = 2; 314 .loc 1 86 0 315 0144 A2410000 lui $2,%hi(b_array) 316 0148 42300000 addiu $2,$2,%lo(b_array) 317 014c 82ED li $3,2 # 0x2 318 014e A8E9 sw $3,32($2) 87:z:/mplab/freertos/src/ztst/cpp_test/main.cpp **** b_array[9] = 3;上記リストで注目すべきは、b_array[7] = 1;の処理コードとb_array[8] = 2;の処理コードは基本的に同じ処理である点です。設定値および出力先のアドレスが異なる以外は同じ処理をしています。同じ処理であるのに、その結果が異なるのはなぜかを調べます。
処理内容がアセンブラであるため、個々のアセンブラ命令の動作を知る必要があります。microMIPS命令セットの動作は下記が参考になります。
参考資料
MIPS Architecture for Programmers Volume II-B: microMIPS32(TM) Instruction Set
Document Number: MD00582
Revision 6.05
December 15, 2016
microMIPS命令セットの説明書です。microMIPS命令セットは一部の命令で命令長が16bitと32bitの二通りの命令を持つものがあります。当然、16bit版では命令長が短いため指定できるレジスタやoffset値に制限があります.。アセンブラではこれらの命令のどちらを使用するかは指定せず、16bit命令が使えるケースでは自動的に16bit命令を出力するようになっているようです。
3.バグ原因を特定する
これらの動作とアセンブラの仕様を突合せすると原因が推定できます。原因は16bit版SW命令のoffset値の取り扱いが誤っていると考えられます。16bit命令が使用されていることはリストの命令バイト数から確認できます。
下記にSW16命令とSW命令の引用を示します。
y
MIPS Architecture for Programmers Volume II-B: microMIPS32(TM) Instruction Seより引用
16bit版のSW命令では、ソースとなるレジスタにoffset値x4の値を加算したアドレスのデータをディストネーション側のレジスタに渡します。このときoffset値は符号なしとして扱われます。offset値の指定は4bit幅なので最大で64byte先までのデータを扱えます。
仮にこのoffset値を符号付と仮定した場合は、先のシミュレータの結果と一致します。つまり、offset値の符号bitが1になった(配列の引数が8を超えた)場合は、ソースのアドレスよりも小さいアドレス側にデータを書き込むことになります。
これまでに経験してきた不可解な現象の幾つかはこのSW16命令によるものと考えられます。不具合が発生する条件は、以下のようになります。
・ SW16命令を使用できる範囲のデータサイズ(64byte以内)に収まっている。
・ データのindex(配列であれば引数の値)が8を超える。
これを避けるくらいならPIC32MXでシミュレーションする方がはるかに楽です。いずれにしてもメーカーによる対応を待つしかないと思われます。