ARM中Bus Error的測試

2020-08-13 14:39:11

在晶片測試的時候,我們有時候會碰到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;
}

上述程式碼中的結構體就是描述了一個堆疊資訊,彙編中會直接將lrpspmsp存入r0~2作爲參數,然後跳轉到_default_isr中實現參數呼叫,這樣lrpspmsp就直接成爲C函數的參數,我們也就可以很方便的使用裏面的內容了,得到這幾個暫存器的值之後我們判斷當前用到的堆疊指針地址,並讓我們定義的結構體指針指向這個地址,之後就可以通過結構體實現對堆疊中內容進行讀寫操作了。

這裏我們就是簡單的把PC加4就可以了。