練習 ARM Cortex-M4 處理器所做的練習項目,專案可見 cm4bmp
1
: Load 2 values from memory, add them and store it to memory by using inline assembly statement將地址 0x20001000
及 0x20001004
分別放入 R1
及 R2
查看 memory view 結果如下
將儲存在 R1
及 R2
地址的資料分別放進 R0
及 R1
預期結果
將 R0
及 R1
資料相加後放進 R0
將 R0
存進 R2
的地址裡
將地址 0x20001004
讀出來並存到 R2
2
: 讀取 Control Register=
: 表示這個指令為寫入,以下範例表示寫入到變數 control_reg
預期結果
3
: 把一個變數的值複製到另一個變數的數值預期結果
4
: 把一個位址的值寫到另一個變數的數值預期結果
目的: 給定 bit band address 及 bit position ,計算其 bit band alias address ,再利用該地址修改目標地址的值
0x20000200
,初始值: 0xff
7
首先將目標地址的資料設定為 0xff
,並使用 bitwise operation 的方法修改其 bit[7] ,對應程式碼如下
接著透過 memory view 可以看到目標地址的資料已經被儲存為 0x7f
(bit[7] 已經被清除)
接著重新初始化目標地址的資料為 0xff
,再使用 bit band 修改,實際程式碼如下
一樣透過 memory view 可以看到目標地址的資料已經被儲存為 0x7f
(bit[7] 已經被清除)
1
: 觀察 SP
從 Linker script 的檔案可以算出一開始 stack 的起始位置 (_estack
)
_estack = 0x20000000 + 64 * 1024 = 0x20010000
可以看到在 startup file , SP
的確為 0x20010000
(畫面左下角),而 MSP
和 SP
值相同,表示 ARM 的預設為 MSP
,以下為 MSP
的數值
msp
0x20010000
2
: SP
的切換練習PSP
MSP
1KB
MSP
和 PSP
各用一半, MSP
使用上半部, PSP
使用下半部以下根據 Cortex™-M4 Devices Generic User Guide 的說明,利用 Control Register 可以從預設 MSP
設定為 PSP
(利用位元 1
)
參考 MRS
and MSR
,可利用指令 MRS
和 MSR
分別讀取和寫入 PSP
接著可以開始進行實驗,首先利用暫存器 R0
將初始值 PSP_START
寫入 PSP
暫存器
此時的 PSP
期望為 0x2000fe00
設定 Control Register 設定使用 PSP
接著藉由函式 add
觀察是否為 PSP
改變,由實驗得知當進入函式 add
後,PSP
的數值從 0x2000fe00
變成了 0x2000fde8
,意味著設定成功
接著透過指令 SVC
觀察進入 Handler mode 時是否使用 MSP
,可以發現 MSP
的值從 0x2000fff8
變成了 0x2000fff0
,意味著當處理器為 handler mode 時,使用 MSP
作為 stack pointer 的來源
上述的程式碼可見 stack.c
Interrupt 1: TIM2 global interrupt
Interrupt 2: I2C1 event interrupt
1
: 當兩個 interrupt 的優先度相同時假設 TIM2
和 I2C1
優先度都為 8
TIM2
interruptI2C
Priority Register enableI2C
優先度和 TIM2
相等,因此不進 I2C
中斷函式,停在 TIM2
函式裡2
: 當兩個 interrupt 的優先度不同時假設 TIM2
優先度為 8
, I2C1
優先度為 7
TIM2
interruptI2C
Priority Register enableI2C
優先度比 TIM2
高,因此跳進 I2C
中斷函式目的: 使用 RTC_WKUP
interrupt 分析進入中斷後,觀察 SP
的變化及儲存的暫存器為何
ARM cortex-M4 exception stack frame
MSP
並觀察其結果進中斷前 SP
的位址: 0x2000fff8
, xPSR
數值: 0x1000000
進中斷後 SP
的位址: 0x2000ffd0
根據下圖,查看儲存資料,並可以得知 ARM 為 little-endian
Stack Pointer (MSP) | Saved Registers | Saved contents |
---|---|---|
0x2000fff4 | xPSR | 0x1000000 |
0x2000fff0 | PC | 0x8000480 |
0x2000ffeC | LR | 0x80005F7 |
0x2000ffe8 | R12 | 0x0 |
0x2000ffe4 | R3 | 0xE000EF00 |
0x2000ffe0 | R2 | 0x3 |
0x2000ffdc | R1 | 0x1 |
0x2000ffd8 | R0 | 0xA |
0x2000ffd4 | Exc Return | 0xFFFFFFF9 |
0x2000ffd0 | 上一個 sp 的值 | 0x2000FFF8 |
EXC
Return
從 assembly code 可以看到進中斷前會先執行 push {r7, lr}
,將 r7
和 lr
的值放進 MSP
的 Stack space
離開中斷則是會執行pop {r7, pc}
,從 stack 取兩個值並分別給 r7
和 pc
PSP
並觀察其結果PSP
)觀察 PSP 管理的 stack 儲存資料
觀察 MSP 管理的 stack 儲存資料,有一些小發現
進中斷和離開中斷都和先前一樣,只差在因為中斷裡使用 MSP
, SP
和 EXEC
會存到 MSP
的空間裡,其他則儲存在 PSP
空間裡
因為 Thread Mode 是使用 PSP
, 因此 EXC Return
的數值也不同
這邊附上 MSP 實際的 memory view
TODO: 補充 warning
目標: Write a program to enable all configurable fault exceptions, implement the fault exception handlers and cause the fault by following method.
參考 Cortex-M4 Devices Generic User Guide Table 4-24 , System Handler Control and State Register (SHCSR
)
1
: 執行未定義指令首先在地址 0x20000501
放進未定義的指令 0xFFFFFF
利用函數指標指到 0x20000501
並且執行,發現會進 Usage Fault
接著會發現有趣的現象,直接進到 UsageFault_Handler
裡
接著開始分析 fault 產生的原因
可以從 Fault Status and Fault Address Register 得知產生原因
查看 UsageFault Status Register 的值可以知道發生什麼問題
最後印出 UFSR 的值可以知道產生原因為 Undefined Instruction ,對照表格後可以看出為 UNDEFINSTR
→ Undefined instruction UsageFault
嘗試更改存放指令的地址為 0x20000500
(T
位元為 0
) ,對照表格後可以看出為 INVSTATE
→ Invalid state UsageFault
開始分析 stack frame
以下為輸出結果
查看 memory view
繼續分析 LR
及 PC
首先是 LR
的部分,從上述的結果可以看到 LR
為 0x8000547
,查看組合語言可以發現指令如下表示
可以得知 nop
為回傳之後要執行的指令
接著分析 PC
,從上述的結果可以看到 PC
為 0x2000501
,查看組合語言結果如下
→ 0x2000501
為 0xFFFFFFFF
,屬於未定義指令
2
: Divide by 0
首先啟用 divide by 0 trap
使用Configuration and Control Register (CCR
)
可利用 CCR
的 bit[4]
來trap divide by 0
以下為程式運行結束後,在終端機顯示的訊息
查看 UFSR
(UsageFault Status Register) ,可以看出產生 Exception 的原因為除以 0
繼續分析 LR
及 PC
首先是 LR
的部分,從上述的結果可以看到 LR
為 0x8000525
,查看組合語言可以發現指令如下所示
執行結束後回傳的地址為 b.n 8000524 <main+0x18>
接著分析 PC
,從上面的結果可以看到 PC
為 0x8000542
,查看 assmembly 可以發現指令為以下表示:
sdiv r3, r2, r3
為進 Exception 前最後執行的指令,可以參考 sdiv instruction
最後嘗試把 trap 除以 0 的功能取消,分析會產生什麼變化,以下為程式執行的結果
查看 HardFault Status Register (HFSR
)
可以看到為 bit[30]
為 1
3
: 從 peripheral region 執行指令Peripheral Region: 從下面的圖,可以看到外部設備的地址位於 0x40000000
~ 0x5FFFFFFF
以下為程式執行的結果
接著查看 MMSR
查看後可以發現原因為 bit[0]
→ 對 XN region 進行 instruction fetch
繼續分析 LR
及 PC
首先是 LR
的部分,從上述的結果可以看到 LR
為 0x800053d
,查看 assmembly 可以發現指令為以下表示:
adds r7, #8
為異常回傳之後第一個執行的指令
接著是 PC
的部分,從上述的結果可以看到 PC
為 0x40000000
,表示 PC
已經跳到 0x40000000
要執行指令
4
: Executing SVC
inside the SVC
handler以下為程式執行後,在終端機顯示的結果,很明顯得產生了 Hard Fault
繼續分析 LR
及 PC
首先是 LR
的部分,從上面的結果可以看到 LR
為 0xfffffff9
,為 EXC
Return 值,表示返回 Thread mode 時使用 MSP
接著是 PC
的部分,從上面的結果可以看到 PC
為 0x8000552
,查看 assmembly 可以發現指令為以下表示:
執行完 SVC
後準備執行 nop
就進 exception
5
: Executing SVC
instruction inside the interrupt handler whose priority whose priority is same or lesser than SVC
handler使用 I2C1_EV
(I2C1
event interrupt & EXTI
Line23 interrupt) 作範例
IRQ number: 31
Priority: 38
SVC
exception
Priority: 3
結果產生了 Hard Fault
繼續分析 LR
及 PC
首先是 LR
的部分,從上面的結果可以看到 LR
為 0xfffffff9
,為 EXC
Return 值,表示返回 Thread mode 時使用 MSP
接著是 PC
的部分,從上面的結果可以看到 PC
為 0x800058e
,查看 assmembly 可以發現指令為以下表示
執行完 SVC
後準備執行 nop
就產生異常了
目標
一共使用 4 種 user tasks
task 介紹
總結: Task 為一段程式碼或是函數,除非被永久移除,不然永遠不會失去其"state"
Thread Mode 時使用 PSP
,進 Handler Mode 後固定為 MSP
Stack assessment
64KB
SRAMStack 設計
1KB
大小的 Stack sizeWhat is state of task?
PC
, MSP
…)Stack of a task: General purpose registers + Some special registers + Status register
ARM Coretex-M4 core register
PC
: 發生 preemptive 時, PC
要儲存當下任務要執行的下一條指令位址Summary: State of a task
r0
~ r12
)PSP
): 因為本次練習使用 PSP
LR
)PC
)PSR
)
Task 切換流程
SysTick
ExceptionPUSH
)PSP
valuePSP
valuePOP
)
Stack Frame
由下圖可以得知 processor 已經自動儲存以下的資訊,因此在 scheduler 裡頭只要儲存剩下的暫存即可
Saving Context(PUSH
)
R4
~ R11
)Retrieve Context(POP
)
R4
~ R11
) ,接著就退出(回傳自動儲存的暫存)PC
為上次中斷的下一個指令位址,因此會開始執行 Task2Task's stack area init and storing of dummy stack frame
1KB
of memory as a private stack可以將 R0
~ R12
初始設為 0
xPSR
EPSR
的 bit[24]
看到 T
bit, T
bit 是用來決定下一個指令為 ARM 或是 Thumb 指令集,而 Cortex-M4 只支援 Thumb 指令集,因此該 bit 應該永遠都要為 1
xPSR
的初始值應該為 0x01000000
LR
EXC_RETURN
, LR
初始值應該為 0xFFFFFFFD
,回傳到 Thread 後使用 PSP
PC
LSB
應該為 1
(因為 T
bit)系統設定
8MHz
HSI
頻率為 8MHz
SysTick
timer count clock = 8MHz (和 HSI
相同)SysTick
timer count clock 從 16MHz 降到 1KHz → Reload value = 16000System Clock 示意圖
HSI
設定reload value
參考 SysTick
Reload Value Register(SYST_RVR
)
儲存在 SYST_RVR
的値需要減 1
SysTick
設定
SYST_CSR
)
ENABLE
設定為 1
: counter enableTICKINT
設定為 1
: 數到 0 之後會產生一個 SysTick
exception requestCLKSOURCE
設定為 1
: 使用 processor clock使用指令 STMDB
將 R4
~ R11
的暫存器數值儲存在 PSP
的 stack 裡
可以參考以下格式
使用指令 LDMIA
取出 Stack
的資料 (R4
~ R11
)