2016-07-27 20 views
2

私のコードでIRQハンドラの戻りアドレスを取得しようとしています。 WDT_IRQHandler()を使用して、ウォッチドッグ・タイマが期限切れになる直前およびデバッグのためのリセット前に、PCの値を保存することを私の目標としています。私はこのアプローチを他のIRQでテストして、そのアイディアを把握しているかどうかを確認しています。 しかし、私はそうしていないようです。ARM Cortex M0での例外の戻りアドレスの取得

私は利用可能なdocumentationを読んでいます。 例外が発生すると、8個のレジスタがスタックにプッシュされることがわかりました。 R0、R1、R2、R3、R12、LR、PCおよびXPSR。

私はまた、スタックが自動的にダブルワードアライメントされていることを読んだことがあります。だから私の考えでは、返信アドレスの取得は次のように簡単です。

  • __builtin_frame_address(0)でspアドレスを取得します。
  • スタックされたPCのオフセット(0x18)を追加し、値を読み取ります。この値は、ハンドラが復帰したときにPCに復元される値です。

デバッガが接続されているかどうかをチェックすると、そのメモリアドレスの内容が常にフラッシュ領域または有効な領域を指しているとは限りません。いずれの場合でも値は決してありませんそのPCはPOP命令の後に引き継がれます。

コードはうまく動作するので、どのように動作するかを理解する上で問題があると思います。これが起こらない他のIRQで

私は解体をチェックすると、いくつかのIRQに定数が飛び出る(?)の前のspに追加され

00001924: 0x000009b0 ...TE_IRQHandler+280 add  sp, #36 ; 0x24 
00001926: 0x0000f0bd ...TE_IRQHandler+282 pop  {r4, r5, r6, r7, pc} 

私は、もっと多くのレジスタがスタックにプッシュされることがありますので、どのようなオフセットでPCを取得するのかをどのように確認できますか?

コードがIRQハンドラにまだ残っているときにSPの周りのメモリダンプをチェックすると、戻りアドレスを見つけることができますが、SPと比較してマイナスのオフセットで常に奇妙な場所にあります。私は正しい住所を得る方法を理解できません。あなたは2つの理由のCハンドラの内部でスタックポインタに頼ることができない

答えて

3

  1. レジスタは常にプリエンプション処理コードのためにアクティブなスタックにプッシュされています。ハンドラは常にメインスタック(MSP)を使用します。割り込みがプロセススタック(PSP)から実行されているスレッドモードコードをプリエンプトすると、レジスタはPSPにプッシュされ、ハンドラスタック内でそれらを見つけることはありません。
  2. Cルーチンは、おそらくローカル変数のためにいくつかのスタック領域を予約していますが、その量はわからないので、レジスタを見つけることができません。

これは、私は通常、それを行う方法である。ここで

void WDT_IRQHandler_real(uint32_t *sp) 
{ 
    /* PC is sp[6] (sp + 0x18) */ 
    /* ... your code ... */ 
} 

/* Cortex M3/4 */ 
__attribute__((naked)) void WDT_IRQHandler() 
{ 
    asm volatile (
     "TST LR, #4\n\t" 
     "ITE EQ\n\t" 
     "MRSEQ R0, MSP\n\t" 
     "MRSNE R0, PSP\n\t" 
     "LDR R1, =WDT_IRQHandler_real\n\t" 
     "BX R1" 
    ); 
} 

/* Cortex M0/1 */ 
__attribute__((naked)) void WDT_IRQHandler() 
{ 
    asm volatile (
     "MRS R0, MSP\n\t" 
     "MOV R1, LR\n\t" 
     "MOV R2, #4\n\t" 
     "TST R1, R2\n\t" 
     "BEQ WDT_IRQHandler_call_real\n\t" 
     "MRS R0, PSP\n" 
    "WDT_IRQHandler_call_real:\n\t" 
     "LDR R1, =WDT_IRQHandler_real\n\t" 
     "BX R1" 
    ); 
} 

トリックはあなたにも別のを使用することができ、私はGCCのASMと裸の機能を使用(ハンドラはアセンブリの小片であるということですasmファイル)を使用して、実際のハンドラにスタックポインタを渡します。ここで(M3/4用)、それがどのように動作するかです:

  • 例外ハンドラ内LRの初期値はEXC_RETURN(詳細here)として知られています。そのビットにはさまざまな意味があります。EXC_RETURN[2]はの場合は0、アクティブなスタックがPSPの場合は1です。
  • TST LR, #4EXC_RETURN[2]をチェックし、条件フラグを設定します。
  • MRSEQ R0, MSPMSPを移動させます(EXC_RETURN[2] == 0)。
  • MRSNE R0, PSP の場合は、PSPR0に移動します。
  • 最後に、LDR/BXが実際の関数にジャンプします(R0が最初の引数です)。

M0/1変種も同様ですが、コアdoes not support IT blocks以降の分岐を使用します。

これはMSP/PSPの問題を解決し、コンパイラが生成したスタック操作の前に実行されるため、信頼できるポインタを提供します。 私はそれの後に何もする必要がなく、LRが既に良いと思っているので、私は単純な(リンクされていない)ブランチを関数に使用しました。それはいくつかのサイクルとLRプッシュ/ポップを節約します。また、使用されるすべてのレジスタはR0-R3のスクラッチ範囲内にあるため、保存する必要はありません。

+0

ありがとうございます。 for point 1)MSPとPSPの両方を__get_MSP()と__get_PSP()を使ってCからチェックできると思います。 for 2)私はプッシュ後に、コードがローカル変数をスタックに追加することができ、コンパイル時にそれらが既知であることを知っているので、固定オフセットに依存することはできません。 私はあなたのアプローチを試みます。 – Vitomakes

+0

@Vitomakes Cからの 'MSP'と' PSP'をチェックしても、どちらが正しいかはわかりません。もちろん、 'MSP'(非常に共通)だけを使用する場合は、チェックをスキップすることができます。 –

+1

"ITE"と "MRSEQ/MRSNE"はcortex M0でサポートされておらず、コンパイラは親指モードにあり、TSTを使用できないことに不満を抱いています。しかし、私は考えを持って、私はそれを並べ替えるでしょう – Vitomakes

関連する問題