在晶片測試的時候,我們有時候會碰到Bus Error的情況,這種情況下程式會進到bus error的中斷中,中斷返回的時候,再次回到原來位置,存取地址的時候再次進入中斷,這樣就造成程式不停進中斷,導致正常測試被打斷。爲了避免這種情況的發生,我們需要修改程式返回的地址,這裏面涉及的東西就有點複雜了,這裏簡單記錄一下。
上面例子中,我們訪問了一個非法的地址產生中斷,程式會將必要的暫存器壓棧之後跳轉到中斷中執行,中斷執行完畢之後,從棧中取出之前的程式指針繼續執行。
這個流程比較簡單,其實問題的解決方法也在這個流程裏面,因爲中斷返回之後還是回到出問題的地址,再次執行引發連續中斷,所以我們只需要將程式返回到的地址加一個偏移,直接讓他到下一條指令就好了(暫時先不管16位元和32位元指令,統一認爲是32位元的指令,即使是16位元指令,我們可以直接跳過兩條指令)。如果想維持程式執行完整性,可以在預期出現中斷的指令後面加若幹NOP
指令。
因爲牽扯到對程式棧的操作,所以一般的C語言是搞不定的,這裏我們就必須通過彙編實現:
typedef struct {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t psr;
} hw_stackframe_t;
void default_isr(void)
{
// set up arguments and call _hardfault_isr
asm("mov r0, lr\n" // arg 0
"mrs r1, psp\n" // arg 1
"mrs r2, msp\n" // arg 2
"b _default_isr\n");
}
static void _default_isr(uint32_t lr, void *psp, void *msp) __attribute__((used));
static void _default_isr(uint32_t lr, void *psp, void *msp)
{
#define VECTORNUM (*(volatile uint8_t*)(0xE000ED04))
hw_stackframe_t *frame;
// Find the active stack pointer (MSP or PSP)
if(lr & 0x4)
frame = psp;
else
frame = msp;
printf("\r\n** HARD FAULT **\r\n\tpc=0x%x\r\n\tmsp=0x%p\r\n\tpsp=0x%p\r\n",
frame->pc, msp, psp);
printf("\r\n****default_isr entered on vector %d*****\r\n",VECTORNUM);
// add PC by 4
frame->pc += 4;
return;
}
上述程式碼中的結構體就是描述了一個堆疊資訊,彙編中會直接將lr
, psp
, msp
存入r0~2
作爲參數,然後跳轉到_default_isr
中實現參數呼叫,這樣lr
, psp
, msp
就直接成爲C函數的參數,我們也就可以很方便的使用裏面的內容了,得到這幾個暫存器的值之後我們判斷當前用到的堆疊指針地址,並讓我們定義的結構體指針指向這個地址,之後就可以通過結構體實現對堆疊中內容進行讀寫操作了。
這裏我們就是簡單的把PC加4就可以了。