お世話になります。
RAMパリティ・エラーの発生について教えていただけるでしょうか。
コンパイラはCC-RLでCS+のコードジェネレータで生成したコードを基本としています。
自前のセクションなどは使用しない場合で,RAMパリティ・エラーが発生することはあり得るでしょうか。
また,cstart.asm 内の _start 直後のコードで,_stkinitがコメントアウトされていますが,スタック領域は初期化しなくても
大丈夫なのでしょうか。
;-------------------------------------------------- ; initializing stack area ;--------------------------------------------------;$IF (__RENESAS_VERSION__ >= 0x01010000); MOVW AX,#LOWW(__STACK_ADDR_END);$ELSE ; for CC-RL V1.00; MOVW AX,#LOWW(_stackend);$ENDIF; CALL !!_stkinit <--- ここです。 ;-------------------------------------------------- ; hardware initialization ;-------------------------------------------------- CALL !!_hdwinit
度々の質問で恐縮ですが,ご教授頂けますようお願いいたします。
ビシ様,チョコ様
アドバイスありがとうございます。
ビシ様の仰る通り②を上にずらしていったところ,while (wait--)の直前の場合は,きれいにH→Lとなっており,
その後リセットしていましたので,wait変数が鍵となりそうです。
逆アセンブルで,この関数の引数を見てみましたが,8bitデータは8bitでアクセスしているように見えましたが,
チョコ様からご指摘頂いた通りスタック領域の初期化を有効にしたところ症状がでなくなりました。
場所は特定できないのですが,チョコ様の言われた「参照時に16ビットで読み出している可能性(実際には8ビット
しか使っていない)」が,論理的な気がいたします。
スタックを初期化するのも気持ちがいいので,これで行きたいと思います。
皆様の的確なアドバイス,本当にありがとうございました。
> このプログラムで読み出しているのは、スタックで渡された引数だけのようです。
CC-RL で R_IICA0_Master_Receive() を呼び出すと各引数は
adr: A
rx_buf: BC
rx_num: DE
wait: X
すべてレジスタで渡されるので違うと思います。
> スタックを初期化するのも気持ちがいいので,これで行きたいと思います。
現象が出なくなっても原因は確認されていないので、別の不具合が生ずる可能性は考えられます。原因の突き止めはやっておいて損はありません。
fujita nozomu様
いつもアドバイスありがとうございます。確かに原因を明らかにしておくのは重要ですね。
R_IICA0_Master_Receive()でエラーが発生しているように見えても,実は何かの割り込みの中ということも
あるかもしれませんね。もう少しがんばってみます。
> ビシ様の仰る通り②を上にずらしていったところ,while (wait--)の直前の場合は,きれいにH→Lとなっており,
> その後リセットしていましたので,wait変数が鍵となりそうです。
INTIIA0 割り込み待ち中にリセットしたということは INTIIA0 割り込み処理の中でパリティエラーが発生している可能性が高いと思います。
チョコです。
CAとCCでかなりコードが違っていましたね。スタックに書き込んでデクリメントと
あったので、CAと勘違いしてしまいました。
ところで、R_IICA0_Master_Receive関数は単に、受信を起動するだけです。受信
完了はどのようにしてチェックしているのでしょうか。
また、パリティ・エラーでのリセットということはRESFレジスタで確認しているの
ですよね。
アドバイスしてくださった皆様,本当にありがとうございます。ようやく原因が特定できましたのでご報告いたします。
やや長文になりますがご容赦ください。
なお,パリティ・エラーの発生は間違いなく,チョコ様の仰る通りリセット時にRESFレジスタを確認して判断しており
ました。(デバッグ用のポートを出力設定してオシロで確認)
先に結論をお伝えすると,関数ポインタを使用してコールした際の memcpy が,未初期化のスタックを参照していました。
以下,詳細をお伝えします。
1.当初,R_IICA0_Master_Receive関数内で発生しているように思えましたが,ジェネレータが作成したコードを世界中の
方々が使用していて問題が報告されていないなら原因は別にあると考え,この関数をコールする直前に永久ループを作成した
ところ,やはりパリティ・エラーが発生。
2.いくつかの割り込みを使用していたので,一つ一つ禁止にしながら実験してタイマー割り込みを特定。
3.タイマー割り込みでコールされる関数の中で,引数に関数ポインタを渡しているものを特定。(自前の関数)
------------------------------------------------------------------------------------------------
typedef void (*Action)(void);
typedef struct
{
//
} TestStruct;
TestStruct Ts1;
void FuncA(TestStruct* this, Action func1, Action func2, Action func3);
static void Func1(void)
}
static void Func2(void)
static void __near r_tau0_channel0_interrupt(void)
FuncA(&Ts1, Func1, Func2, 0);
4.タイマー割り込み内で上のFuncAをコールする箇所を逆アセンブルで確認したところ,memcpy内で未初期化の
スタックを参照していることが判明
SUBW SP,#1CH
MOVW AX,#3C19H ---> Func1のアドレス
MOVW [SP+2H],AX ---> SP+2Hが19H,SP+3Hが3CH
MOV [SP+4H],#0H ---> SP+4Hが0H,SP+5Hは未初期化
MOVW AX,#3C1DH ---> Func2のアドレス
MOVW [SP+0CH],AX ---> SP+0CHが19H,SP+0DHが3CH ①
MOV [SP+0EH],#0H ---> SP+0EHが0H,SP+0FHは未初期化
CLRW AX ---> 3つめの引数はnull
MOVW [SP+18H],AX ---> SP+18Hが00H,SP+19Hが00H ②
MOV [SP+1AH],#0H ---> SP+1AHが0H,SP+1BHは未初期化
MOV A,[SP+4H]
MOV [SP+0H],A
MOVW AX,[SP+2H]
MOVW [SP+6H],AX
SUBW SP,#4H
MOVW AX,SP
ADDW AX,#1CH ---> AXは上の②のSP+18Hを指している
MOVW BC,AX
MOVW DE,#4H ---> 未初期化のSP+1BHも含めて4バイト分をmemcpy ③
CALL !!_memcpy
ADDW AX,#14H ---> AXは上の①のSP+0CHを指している
MOVW DE,#4H ---> 未初期化のSP+0FHも含めて4バイト分をmemcpy ④
MOV A,[SP+8H]
MOV C,A
MOVW AX,[SP+0EH]
MOVW DE,AX
MOVW AX,#0D008H
CALL !!_FuncA
③と④の memcpy の中では4回のループで1バイトずつコピーが実行されており,あきらかに未初期化のスタックを参照
していました。
わたしの関数ポインタの使用方法に注意が足りない部分もありそうですが,これで,スタックを初期化すればパリティ・エラー
が発生しない理由をはっきりさせることができたと思います。fujita nozomu様が教えてくださった memcpy の情報に少し
通ずるところもありそうです。
皆様のアドバイスのおかげで原因を究明でき本当にありがとうございました。
あとはこの現象を回避する方法ですが,スタックの初期化で行こうと思いますが,もし他にも良い方法がありましたらご教授
いただければ幸いです。
標準の cstart.asm ではスタック領域を初期化しないにもかかわらず未初期化の領域を memcpy() してしまうというのは CC-RL の現在のところの不具合なのではないかとおもいますが、それにしても随分効率の悪いコードを吐いてますね。
解決方法としては Action の定義を
typedef void (__near*Action)(void);
に変更し、かつ Action に渡される関数の配置を near に変更するか、あるいはコードの全体が 64kB に収まっているならば CC-RL(ビルド・ツール)のプロパティ → 「コンパイル・オプション」のタブの「メモリ・モデル」の項目「メモリ・モデル」の設定を「スモール・モデル(-memory_model=small)」に変更するのが簡単だと思います。
上記の変更で、例えば r_tau0_channel0_interrupt() のコードは下記のように効率化され、本来不要な筈の memcpy() も呼ばれなくなるのでこの箇所でのパリティエラーは発生しなくなると思われます。
void __near r_tau0_channel0_interrupt(void) _r_tau0_channel0_interrupt: f6 CLRW AX FuncA(&Ts1, Func1, Func2, 0); c1 PUSH AX ; 0 340d03 MOVW DE,#30DH ; Func2() のアドレス 320c03 MOVW BC,#30CH ; Func1() のアドレス 300caf MOVW AX,#0AF0CH ; Ts1 のアドレス fd2101 CALL !_FuncA c6 POP HL d7 RET
> ROM化した場合にのみ発生しています。
デバッガではデバッガが使用する分スタック領域を余計に使用するので、アプリケーションが使用する以前にデバッガがスタック領域を「初期化してくれてた」のかもしれないですね。
ご提案に感謝いたします。また,実際にコードを試してくださりありがとうございます。
スモールモデルに変更したところmemcpy() がなくなり,fujita nozomu様が試してくださったのと同じスッキリした
コードになりました。もちろん,ROM化してもパリティエラーは発生しなくなりました。
現在試作で使用しているのがG14のコードフラッシュ96KB品なんですが,本作では64KB品にするのでちょうど良かった
です。なぜあのmemcpy()が挿入されているのか理由はわかりませんが,もしかして最初から64KB品でデバッグしたら
こんな苦労はしなかったんでしょうかね。
それでも皆様のアドバイスのおかげで原因を特定し解決できたのでホントにこれですっきりです。
ありがとうございました。