---
# System prepended metadata

title: ARM Cortex M

---

# ARM Cortex M

## HardFault Handling
**Analyze Hard Fault**
當Exception發生時，如何找到Exception發生的位置？

| EXC_RETURN | 條件                     | 
| ---------- | ----------------------- |
| 0xFFFFFFF1 | 返回handler mode(巢狀中斷)|
| 0xFFFFFFF9 | 返回thread mode，使用msp  |
| 0xFFFFFFFD | 返回thread mode，使用psp  |

![](https://i.imgur.com/NZm0U2n.png)

**實驗**
1. 定義0xFFFFFFFF位址。
```cpp=
#define INVALID_ADDR (*(volatile uint32_t *)0xFFFFFFFF)
```

2. 嘗試放在某個程式段中，會發生Hard Fault。
```c=+
INVALID_ADDR = INVALID_ADDR + 1;
```

3. 判斷目前的Stack Pointer是MSP還是PSP，另外儲存LR。
```c=+
__asm void HardFault_Handler(void)
{
    tst    lr, #4
    ite    eq
    mrseq  r0, msp
    mrsne  r0, psp
    mov    r1, lr
    b      __cpp(HardFault_Handler_C)
}
```

4. 倒出Stacking的Stack Memory資訊。
```c=+
void HardFault_Handler_C(unsigned int *hardfault_args, unsigned int lr_value)
{
    unsigned long stacked_r0;
    unsigned long stacked_r1;
    unsigned long stacked_r2;
    unsigned long stacked_r3; 
    unsigned long stacked_r12;
    unsigned long stacked_lr;
    unsigned long stacked_pc;
    unsigned long stacked_psr;
 
    stacked_r0 =  ((unsigned long)hardfault_args[0]);
    stacked_r1 =  ((unsigned long)hardfault_args[1]);
    stacked_r2 =  ((unsigned long)hardfault_args[2]);
    stacked_r3 =  ((unsigned long)hardfault_args[3]);
    stacked_r12 = ((unsigned long)hardfault_args[4]);
    stacked_lr =  ((unsigned long)hardfault_args[5]);
    stacked_pc =  ((unsigned long)hardfault_args[6]);
    stacked_psr = ((unsigned long)hardfault_args[7]);  
    
    printf("[HardFault]\r\n");
    printf("Stack frame:\r\n");
    printf(" R0 = %x\r\n",  stacked_r0);
    printf(" R1 = %x\r\n",  stacked_r1);
    printf(" R2 = %x\r\n",  stacked_r2);
    printf(" R3 = %x\r\n",  stacked_r3);
    printf(" R12 = %x\r\n", stacked_r12);
    printf(" LR = %x\r\n",  stacked_lr);
    printf(" PC = %x\r\n",  stacked_pc);
    printf(" PSR = %x\r\n", stacked_psr);    

    printf(" LR/EXC_RETURN = %x\r\n", lr_value);
    while(1);
}
```

**實驗結果**

![](https://i.imgur.com/s5UvPz8.png)

執行到LDR r0, [r0, #0x00]會造成Hard Fault。


## Why Use "volatile"

**Compiler開O0和O3的差異**

{%youtube YqWIKpjQAmM %}

**分析組語**
分析assembly，開O3結果變成重複一直跳0x080029f8。

![](https://i.imgur.com/g8ORU6K.png)

**原因**
Compiler會因為要加速程式執行的performance，把0x20000060位址的數值暫存在CPU Register(R0~R12)中，不再從Memory裡載入到CPU Register(R0~R12)中，導致value值沒有被更新。因此使用volatile來避免Compiler過度優化。


## Variable Length Argument
```c=
include <stdarg.h> // 需要include stdarg.h
```

```c=
static void printmsg(char *msg, ...)
{
    char str[80];
    va_list args;

    va_start(args, msg);
    vsprintf(str, msg, args);

    for(uint32_t i = 0; i < strlen(str); i++) {
        while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) != SET);
        USART_SendData(USART2, str[i]);
    }
    while(USART_GetFlagStatus(USART2, USART_FLAG_TC) != SET);
    va_end(args);
}
```

結果

![](https://i.imgur.com/TPhLAtz.png)