《CORTEX-M4知識點總結(jié).doc》由會員分享,可在線閱讀,更多相關(guān)《CORTEX-M4知識點總結(jié).doc(6頁珍藏版)》請在裝配圖網(wǎng)上搜索。
Cortex-M4內(nèi)核知識點總結(jié)
余
明
目錄
Cortex-M4內(nèi)核知識點總結(jié) 1
1 ARM處理器簡介 4
2 架構(gòu) 5
2.1架構(gòu)簡介 5
2.2編程模型 5
2.3存儲器系統(tǒng) 8
2.4復(fù)位和復(fù)位流程 11
3 指令集 13
3.1 CM4指令集特點 13
3.2 Cortex-M處理器間的指令集比較 13
3.3 匯編指令簡要介紹 13
3.3.1 處理器內(nèi)傳送數(shù)據(jù) 13
3.3.2 存儲器訪問指令 14
3.3.3 算數(shù)運算 15
3.3.4 邏輯運算 16
3.3.5 移位 16
3.3.6 異常相關(guān)指令 16
4 存儲器系統(tǒng) 17
4.1 存儲器外設(shè) 17
4.2 Bootloader 17
4.3位段操作 18
4.4 存儲器大小端 18
5 異常和中斷 20
5.1 中斷簡介 20
5.2異常類型 20
5.3 中斷管理 21
5.4 異?;蛑袛嗥帘渭拇嫫?22
5.4.1 PRIMASK 22
5.4.2 FAULMASK (M0中無) 22
5.4.3 BASEPRI(M0中無) 22
5.5 中斷狀態(tài)及中斷行為 22
5.5.1 中斷狀態(tài) 22
5.5.2 中斷行為 23
5.6 各Cortex-M處理器NVIC差異 25
6 異常處理 27
6.1 C實現(xiàn)的異常處理 27
6.2 棧幀 27
6.3 EXC_RETURN 28
6.4異常流程 29
6.4.1 異常進(jìn)入和壓棧 29
6.4.2 異常返回和出棧 30
7 低功耗和系統(tǒng)控制特性 31
7.1 低功耗模式 31
7.1 SysTick定時器 31
8 OS支持特性 33
8.1 OS支持特性簡介 33
8.2 SVC和PendSV 33
8.3 實際的上下文切換 34
1 ARM處理器簡介
ARM處理器的種類很多,從手機上的高端處理器芯片到面向微控制器的芯片,都有ARM的身影。2011年基于ARM處理器的芯片的出貨量已經(jīng)到達(dá)79億。這一章首先對ARM處理器有個簡單的了解。
在早期的時候,ARM處理器使用后綴表明特性。例如ARM7TDMI,T表示支持Thumb指令,D表示JTAG,M表示快速乘法器,I則表示嵌入式ICE模塊。
近幾年,ARM改變處理器的命名方式,統(tǒng)一使用了Cortex處理器的名稱。Cortex處理器下分為三類:
Cortex-A系列:需要處理高端嵌入式系統(tǒng)等復(fù)雜應(yīng)用的應(yīng)用處理器
Cortex-R系列:實時、高性能的處理器,面向較高端的實時市場
Cortex-M系列:面向微控制器和混合信號設(shè)計等小型應(yīng)用,注重低成本、低功耗。
不同系列的處理器使用不同版本的架構(gòu)
Cortex-A系列
ARMv7-A架構(gòu)
Cortex-R系列
ARMv7-R架構(gòu)
Cortex-M系列
ARMv7-M、ARMv6-M
在Cortex-M系列中,進(jìn)一步都處理器進(jìn)行了劃分
處理器
功能
架構(gòu)
Cortex-M0、Cortex-M0+
低功耗
ARMv6-M
Cortex-M1
FPGA
ARMv6-M
Cortex-M3
微控制器
ARMv7-M
Cortex-M4
增加DSP
ARMv7E-M
2 架構(gòu)
2.1架構(gòu)簡介
Cortex-M3和Cortex-M4處理器都是基于ARMv7-M架構(gòu)。最初ARMv-7M架構(gòu)是隨著Cortex-M3處理器一同引進(jìn)的,而在Cortex-M4發(fā)布時,架構(gòu)中又額外增加了新的指令和特性,改進(jìn)后的架構(gòu)有時也被稱為ARMv7E-M。
2.2編程模型
2.2.1操作模式和狀態(tài)
Cortex-M4處理器包括兩種操作狀態(tài)和模式,還有兩種訪問等級。
1. 操作狀態(tài)
調(diào)試狀態(tài):處理器被暫停后,就會進(jìn)入調(diào)試狀態(tài),比如利用調(diào)試器觸發(fā)斷點,單步執(zhí)行等。
Thumb狀態(tài):處理器執(zhí)行程序代碼,它就是處在Thumb狀態(tài),因為Cortex-M4用的是Thumb指令,所以稱為Thumb狀態(tài),并且在Cortex-M處理器中已經(jīng)不支持ARM指令,也就不存在ARM狀態(tài)。
2. 操作模式
處理模式:執(zhí)行中斷服務(wù)程序等異常處理。在處理模式下,處理器總是具有特權(quán)訪問等級。
線程模式:執(zhí)行普通的程序代碼。
3. 訪問等級
特權(quán)訪問等級:可以訪問處理器中的所有資源。
非特權(quán)訪問等級:有些存儲器區(qū)域無法訪問,有些操作也無法使用。
訪問等級有特殊寄存器CONTROL控制。軟件可將處理器從特權(quán)訪問等級轉(zhuǎn)換至非特權(quán)訪問等級,但反之無法直接轉(zhuǎn)換,需要借助異常機制。
處理器的操作模式和狀態(tài)可由圖1.1來表示,在上電后,默認(rèn)處于特權(quán)線程模式下的Thumb狀態(tài)。
2.2.2 寄存器
對于ARM架構(gòu)來講,處理存儲器中的數(shù)據(jù)時,需將其從存儲器加載到寄存器中,處理完畢后,若有必要,還可以再寫回存儲器。這種方式被稱作“加載-存儲架構(gòu)”(LOAD-STORE)。
Cortex-M4處理器的寄存器組中有16個寄存器,其中包括13個通用寄存器和3個有特殊用途的寄存器。
1 通用寄存器R0-R12
R0-R7被稱作低寄存器,許多16位指令只能訪問低寄存器。R8-R12稱作高寄存器,可用32位指令和幾個16位指令訪問。R0-R12初始值未定義。
2 棧指針R13
R13為棧指針,可通過PUSH和POP操作實現(xiàn)棧存儲的訪問。棧指針包括兩個:主棧指針MSP和進(jìn)程棧指針PSP。MSP為默認(rèn)指針,復(fù)位后或處理模式時只能是MSP,而PSP只能在線程模式使用。棧指針的選擇有CONTROL寄存器控制。
MSP和PSP的最低兩位必須是0,也就是棧指針的地址操作必須4字節(jié)對齊。
3 鏈接寄存器(LR)R14
用于函數(shù)或子程序調(diào)用時返回地址的保存,在異常中則用來保存進(jìn)異常前狀態(tài)信息,包括系統(tǒng)模式、棧指針模式等。異常返回時參考LR中的信息返回到相應(yīng)狀態(tài)。
4 程序計數(shù)器(PC)R15
R15為程序計數(shù)器,讀操作返回當(dāng)前地址加4,寫操作引起跳轉(zhuǎn)。
2.2.3 特殊寄存器
特殊寄存器有三類:程序狀態(tài)寄存器、中斷/異常屏蔽寄存器、處理器控制寄存器。
1 程序狀態(tài)寄存器:應(yīng)用PSR(APSR)、執(zhí)行PSR(EPSR)、中斷PSR(IPSR)。三個寄存器可以單獨訪問,也可以組合到一個寄存器中訪問。
在APSR中包含N(負(fù)標(biāo)志)、Z(零標(biāo)志)、C(進(jìn)位標(biāo)志)、V(溢出標(biāo)志)、Q(飽和標(biāo)志)和GE(大于或等于標(biāo)志,只在M4中有)。
IPSR中是中斷號,只讀。
EPSR中,T為表示Thumb狀態(tài),由于M4支持Thumb狀態(tài),不支持ARM狀態(tài),T位始終為1。ICI是中斷繼續(xù)指令位,保存的是中斷被打斷時的信息。IT指令時IF-THEN指令,用于條件執(zhí)行。
2 PRIMASK、FAULTMASK、和BASEPRI寄存器:這三個寄存器只能在特權(quán)模式下使用。PRIMASK可屏蔽除NMI和HardFault之外的所有異常。FAULTMASK還可屏蔽HardFault。BASEPRI可以根據(jù)設(shè)置屏蔽低優(yōu)先級的中斷,可控制8個或16個中斷,相應(yīng)的改寄存器的寬度為3位或4位。
3 CONTROL寄存器
CONTROL寄存器主要有以下幾項作用:
線程模式下的訪問等級
指針的選擇
當(dāng)前代碼是否使用了浮點單元
分別對應(yīng)了寄存器的低三位
位
功能
nPRIV(第0位)
0對應(yīng)特權(quán)等級,1為非特權(quán)等級
SPSEL(第1位)
0對應(yīng)主棧指針,1為進(jìn)程棧指針,處理模式下始終為0。
FPCA(第2位)
0未使用浮點,1使用了浮點
2.2.4 浮點寄存器
1 S0-S31和D0-D15
S0-S31都為32位寄存器,也可以D0-D15的方式成對訪問,但M4不支持雙精度浮點運算,只是可以傳輸雙精度數(shù)據(jù)。
2 浮點狀態(tài)和控制寄存器(FPSCR)
FPSCR兩個功能
提供浮點運算結(jié)果的狀態(tài)信息,如負(fù)標(biāo)志、進(jìn)位標(biāo)志等。
定義一些浮點運算動作,如何舍入等
3 經(jīng)過存儲器映射的浮點單元控制寄存器(CPACR)
該寄存器經(jīng)過了映射,也就是說需要通過通用寄存器加載進(jìn)行設(shè)置,寄存器的功能是可以設(shè)置浮點單元的訪問權(quán)限,拒絕訪問、特權(quán)訪問,全訪問。
2.3存儲器系統(tǒng)
2.3.1 存儲器系統(tǒng)特性
4GB線性地址空間
架構(gòu)定義的存儲器映射。4GB的存儲器空間被劃分為多個區(qū)域,用于預(yù)定義的存儲器和外設(shè)。
支持大端和小端的存儲器系統(tǒng)。
位段訪問。
寫緩沖
存儲器保護(hù)單元MPU
非對齊傳輸支持
2.3.2 存儲器映射
CORTEX-M處理器的4GB地址空間被分為了多個存儲器區(qū)域,如圖所示。區(qū)域根據(jù)各自典型用法進(jìn)行劃分,他們主要用于:
程序代碼訪問(如CODE區(qū)域)
數(shù)據(jù)訪問(如SRAM區(qū)域)
外設(shè)(如外設(shè)區(qū)域)
某款芯片的存儲器映射分配
一般Code放在Flash當(dāng)中,數(shù)據(jù)放在RAM中。數(shù)據(jù)在RAM存放有一定的順序,可以分為數(shù)據(jù)段,BSS段、堆和棧區(qū)域。
數(shù)據(jù)段,存儲在內(nèi)存的底部,包含初始化的全局變量和靜態(tài)變量。
BSS段,未初始化的數(shù)據(jù)。
堆,C函數(shù)自動分配存儲器區(qū)域,例如alloc()和malloc()。
棧,用于臨時數(shù)據(jù)存儲,局部變量,函數(shù)調(diào)用
2.3.3 棧存儲
同幾乎所有的處理器架構(gòu)一樣,Cortex-M處理器在運行時需要棧存儲和棧指針R13。ARM處理器將系統(tǒng)主存儲器用于棧空間操作,使用PUSH指令往棧中存儲數(shù)據(jù)以及POP指令從棧中提取數(shù)據(jù)。
處理器使用的是滿遞減的模型,棧指針是向下增長的。處理器啟動后,SP被設(shè)置為棧存儲空間的最后的位置,也就是最低位置,PUSH時,SP指針首先減小,然后將數(shù)據(jù)壓入棧中。POP的時候相反,先將當(dāng)前SP所指的數(shù)據(jù)出棧,然后再修改SP,SP此時增大??捎孟旅鎯煞鶊D加以理解,
棧中主要用于:
存儲局部變量
異常產(chǎn)生時保存處理器狀態(tài)(LR、xPSR)和寄存器數(shù)值
函數(shù)調(diào)用時
2.4復(fù)位和復(fù)位流程
對于典型的Cortex-M處理器,復(fù)位類型有三種:
上電復(fù)位。復(fù)位微控制器中所有部分。
系統(tǒng)復(fù)位。只會復(fù)位處理器和外設(shè),不包括處理的調(diào)試支持部件
處理器復(fù)位。只復(fù)位處理器。
在復(fù)位后以及處理器開始執(zhí)行程序前,處理器會從存儲器中讀出頭兩個字節(jié)。第一個字表示主棧指針的初始值。第二個字代表復(fù)位處理起始地址的復(fù)位向量。處理器讀出這兩個自己后,就會將這些數(shù)值賦給MSP和PC。
之前在棧存儲時講到過,Cortex-M處理器的棧操作時基于滿遞減的,所以SP的初始值應(yīng)該設(shè)置在棧頂?shù)奈恢谩@?,若存儲器區(qū)域為0x20007C000~0x20007FFF(1KB),初始的棧指針就應(yīng)該為0x20008000。
另外,對于Cortex-M處理器,向量表中向量地址的最低位應(yīng)該為1,以代表他們?yōu)門humb狀態(tài)。對于下圖中的例子,復(fù)位向量為0x101,而啟動代碼是從0x100開始的。在取出復(fù)位向量后。Cortex-M處理器就可以從復(fù)位向量地址處執(zhí)行程序,并開始正常操作。
3 指令集
CORTEX-M4使用的是Thumb-2指令集,不支持ARM指令集,Thumb指令集是ARM指令集的子集,但是Thumb-2技術(shù)已經(jīng)不再支持ARM狀態(tài)。
CORTEX-M處理器間的一個區(qū)別就是指令集特性。為了將回路面積降到最低,CORTEXM0、CORTEXM0+、CORTEXM1處理器只支持多數(shù)16位指令和部分32位指令,CORTEX-M3支持的32位指令更多。CORTEX處理器支持剩下的SIMD(單指令多數(shù)據(jù))等DSP提升指令集可選的浮點指令。
3.1 CM4指令集特點
CM4處理器使用ARMv7-M架構(gòu),指令集為Thumb指令集中的Thumb-2技術(shù),具有如下特點
16位與32位混合指令
加載/存儲指令集,不能直接操作存儲器。
指令長度可變,使用16/32位由功能決定,優(yōu)先使用16位。
DSP指令,CM4中為單精度,CM7中可以雙精度
3.2 Cortex-M處理器間的指令集比較
Cortex-M處理器的架構(gòu)有三類,ARMv6-M,ARMv7-M,ARMv7E-M。
內(nèi)核
性能
ARMv6-M
M0/M0+/M1
一般數(shù)據(jù)處理,IO控制人物
ARMv7-M
M3
高級數(shù)據(jù)處理、硬件除法
ARMv7E-M
M4
SIMD、快速MAC飽和運算
3.3 匯編指令簡要介紹
3.3.1 處理器內(nèi)傳送數(shù)據(jù)
MOV
< Rm源寄存器>
源寄存器處可以是立即數(shù),立即數(shù)為8位以下,9-16位用MOVW,32位的需要使用LDR偽指令。
使用浮點單元時可以使用VMOV指令。
3.3.2 存儲器訪問指令
訪問可分為讀和寫指令,另外根據(jù)讀寫的大小還有其他的延伸。
數(shù)據(jù)類型
加載(讀)
存儲(寫)
8位無符號
LDRB
STRB
8位有符號
LDRSB
STRB
16位無符號
LDRH
STRH
16位有符號
LDRSH
STRH
32位
LDR
STR
多個32位
LDM
STM
64位
LDRD
STRD
棧操作
PUSH
POP
介紹幾個較為重要的
1 LDR/STR
LDR Rd,[Rn,#offset] 從存儲器Rn+offset處讀取字,讀取到Rd中
STR Rd,[Rn,#offset] 向存儲器Rn+offset處存儲字,數(shù)據(jù)來自Rd。
LDR R0,[R1,#0X08] 從存儲器R1+0x08處讀取字,放到R0中
支持寫回功能,加!即可,上面可以寫成
LDR R0,[R1,#0X08]! 這樣表示存儲器位置的R1被更新為R1+0x08
2 LDM/STM 讀/寫多個字
上述命令是為了從存儲器中讀寫多個字。一般會加后綴配合使用
LDMIA Rn,
Rn是存儲器位置,reg list是寄存器列表,從Rn所指的存儲器位置讀取數(shù)據(jù),放入寄存器中,每次讀取完成后,地址就會自動加4。作用相當(dāng)于POP。另外要注意的是,先讀取的數(shù)據(jù)放置在低寄存器中,后讀取的數(shù)據(jù)放置到高寄存器。
STMIA Rn,
Rn是存儲器位置,reg list是寄存器列表, 向Rn所指的存儲器位置存儲數(shù)據(jù),每次存儲前,地址自動減4。相當(dāng)于PUSH操作。另要注意的是,先存儲高寄存器的數(shù)據(jù),后存儲低寄存器的數(shù)據(jù)。
同樣,這兩個指令都可以通過!表示寫回操作,更新寄存器所指的存儲器位置
3 壓棧與出棧 PUSH/POP
PUSH和POP和上面的LDMIA和STMDB是相同的。
3.3.3 算數(shù)運算
加:ADC
減:SUB
乘:MUL
除:DIV
對此不做詳細(xì)介紹
3.3.4 邏輯運算
與:AND
或:ORR
位清除:BIC
按位異或:EOR
按位或非:ORN
3.3.5 移位
算數(shù)右移:ASR
邏輯左移:LSL
邏輯右移:LSR
循環(huán)右移:ROR
3.3.6 異常相關(guān)指令
之前說過,M4可以有特權(quán)模式和非特權(quán)模式,并且非特權(quán)模式不能直接轉(zhuǎn)換到特權(quán)模式,只能在異常中修改CONTROL寄存器。這里就可以通過SVC指令來進(jìn)入異常。
SVC #
這樣就可以進(jìn)入SVC中斷中,然后修改CONTROL寄存器。要注意的是,調(diào)用SVC指令后,需盡快進(jìn)入中斷中,如果有其他高優(yōu)先級的中斷打斷了SVC,就會引起HardFault。
CPS指令使用時需要帶上后綴:IE(中斷使能),ID(中斷禁止),還需指定要設(shè)置的中斷屏蔽寄存器,如之前講到的PRIMASK和FAULTMASK
指令
操作
CPSIE I
使能中斷(清除PRIMASK)
CPSID I
禁止中斷(設(shè)置PRIMASK),除NMI和HardFault
CPSIE F
使能中斷(清除FAULTMASK)
CPSID F
禁止中斷(設(shè)置FAULTMASK),除NMI
4 存儲器系統(tǒng)
4.1 存儲器外設(shè)
哈佛結(jié)構(gòu),程序存儲器和數(shù)據(jù)存儲器分開,也就是指令和數(shù)據(jù)可以同時訪問。
1、在第一章中的存儲器映射圖中,0-0.5G為代碼段,主要用于程序代碼,改區(qū)域一般也允許數(shù)據(jù)訪問。一般此處為Flash。
在keil中,代碼編譯后,整個代碼分為幾部分:Code(代碼),RO-data(只讀數(shù)據(jù)),RW-data,(初始化的可讀寫變量大?。?,ZI-data(Zero Initialize)未初始化的可讀寫變量大小,它會被自動初始化為0。ZI-data不會被算到代碼里,因為它不會被初始化。
簡單來說呢就是在燒寫的時候FLASH中的被占用的空間為:Code+RO-data+RW-data。
程序運行的時候,芯片內(nèi)部RAM使用的空間為:RW-data+ZI-data
2、0.5G-1G范圍內(nèi)是SRAM,主要用于連接SRAM,其大都為片上SRAM,不過對存儲器的類型沒有什么限制。若支持可選的位段特性,則SRAM區(qū)域的第一個1MB可位尋址,還可以在這塊區(qū)域中執(zhí)行程序代碼。
3、1G-1.5G是外設(shè)區(qū)域,多用于片上外設(shè),和SRAM區(qū)域類似,也可以放置程序代碼,若支持可選的位段特性,則外設(shè)區(qū)域的第一個1MB是可選的。
4、1.5G-2.5G空間為外部RAM空間
5、2G-3G空間為設(shè)備空間,用于片外外設(shè)。
6、3G-4G空間為系統(tǒng)空間。
4.2 Bootloader
芯片設(shè)計人員將Bootloader放入系統(tǒng)中的原因是多方面的。例如:
提供Flash編程功能,這樣就可以利用一個簡單的UART接口來編程Flash,或者當(dāng)程序運行時,在自己的應(yīng)用程序中編程Flash存儲器的某些部分。
提供通信協(xié)議棧等額外的固件,可被軟件開發(fā)人員通過API調(diào)用。
提供芯片內(nèi)置的自檢功能(BIST)
比如在1601中,提供一個4K的info區(qū),和128K的main區(qū),4K的info區(qū)就是一個bootloader,提供SPI下載功能,利用撥碼開關(guān)可以設(shè)置從哪里啟動,mode=0時從info區(qū)啟動,mode=1時從main區(qū)啟動,并且main區(qū)分兩部分,軟件可設(shè)置從低64K啟動還是從高64K啟動。這里設(shè)計存儲器重映射的問題,系統(tǒng)啟動時是從0X00開始的,不管是Boot loader還是用戶flash,都得從0x00開始,然后0x04放的reset_handler的地址,mode=0,那infor區(qū)就被映射到了0x00,mode=1,main區(qū)就被映射到了0x00。
4.3位段操作
對存儲器中某一位操作是如何實現(xiàn)的呢?先來看看普通模式下,
寫某一位:
LDR R0,=0X200000000 ;設(shè)置地址
LDR R1,[R0] ;讀數(shù)據(jù)
ORR.W R1,#0X04 ;修改第2位
STR R1,[R0] ;寫回
讀某一位:
LDR R0,=0X200000000 ;設(shè)置地址
LDR R1,[R0] ;讀數(shù)據(jù)
UBFX.W R1, R1,#2,#1;提取第2位
這種操作無法保證原子性,比如輸出端口的第0位被主程序使用,而第一位被中斷使用,這樣有可能出現(xiàn)數(shù)據(jù)沖突。位段操作模式下,這種現(xiàn)象可以避免,因為位段操作是在硬件等級下修改的。
位段操作只在兩個區(qū)域支持,SRAM的第1MB,外設(shè)區(qū)域的第1MB。每1MB會對應(yīng)一個32M的區(qū)域,只需操作這32M的某個字,就能對應(yīng)那1MB區(qū)域的某一位。例如寫0x22000008為1,就設(shè)置了0x20000000第3位為1。在指令上也會更加簡化。
位段寫操作
LDR R0, =0X2200000 ;設(shè)置add
MOV R1,#1 ;要寫的數(shù)據(jù)
STR R1 ,[R0] ;寫
設(shè)置了位段操作模式,對應(yīng)的32MB區(qū)域?qū)⒉荒茉偈褂谩?
4.4 存儲器大小端
大小端指的是數(shù)據(jù)存儲時的順序問題。大端指的是高字節(jié)的數(shù)據(jù)放在低地址中,低字節(jié)放在高地址中,這種方式符合人類思維。小端則是低字節(jié)放在低地址中,高字節(jié)放在高地址中,這種方式更符合計算機思維。
例如將0x12345678放到存儲器0x2000-0x2003地址處
大端方式
地址
0x2003
0x2002
0x2001
0x2000
數(shù)據(jù)
0x78
0x56
0x34
0x12
小端方式
地址
0x2004
0x2003
0x2002
0x2001
數(shù)據(jù)
0x12
0x34
0x56
0x78
CM4處理器同時支持小端和大端的存儲器系統(tǒng)。CM的微控制器大多是小端的。
5 異常和中斷
5.1 中斷簡介
所有的CORTEX-M處理器都會提供一個用于中斷處理的嵌套向量中斷控制器,也就是NVIC。中斷也屬于異常的一種,其他異常包括如錯誤異常和其他用于OS支持的系統(tǒng)異常。
M4的NVIC支持最多240個IRQ(中斷請求),1個不可屏蔽中斷(NMI),1個SysTick(系統(tǒng)節(jié)拍)定時中斷及多個系統(tǒng)異常。
異常編號
異常類型
優(yōu)先級
描述
1
復(fù)位
-3
復(fù)位
2
NMI
-2
不可屏蔽中斷
3
硬件錯誤
-1
硬件錯誤
4
MemManage錯誤
可編程
存儲器管理錯誤
5
總線錯誤
可編程
總線錯誤
6
使用錯誤
可編程
程序錯誤
7-10
保留
11
SVC
可編程
請求管理調(diào)用
12
調(diào)試監(jiān)控
可編程
調(diào)試監(jiān)控
13
保留
14
PendSV
可編程
一般用于上下文切換
15
SYSTICK
可編程
系統(tǒng)節(jié)拍定時器
16
外部中斷#0
可編程
片上外設(shè)或外部中斷源產(chǎn)生
17
外部中斷#1
可編程
…
…
…
255
外部中斷#239
可編程
除了前3個異常的優(yōu)先級是固定的,其余異常都可以修改優(yōu)先級。
5.2異常類型
編號1-15為系統(tǒng)異常,16及以上的則為中斷輸入。
5.3 中斷管理
為了簡化中斷和異常管理,CMSIS-Core提供了多個訪問函數(shù)。
函數(shù)
用法
Void NVIC_EnableIRQ(IRQn_Type IRQn)
使能外部中斷
Void NVIC_DisableIRQ(IRQn_Type IRQn)
禁止外部中斷
Void NVIC_SetPriority(IRQn_Type IRQn,uint32_t priority)
設(shè)置中斷的優(yōu)先級
Void _enable_irq(void)
清除PRIMASK使能中斷
Void _disable_irq(void)
設(shè)置PRIMASK禁止所有中斷
Void NVIC_SetPriorityGrouping(uint32_t priorityGroup)
設(shè)置優(yōu)先級分組
復(fù)位后,所有中斷都處于禁止?fàn)顟B(tài),且默認(rèn)的優(yōu)先級為0。在使用任何一個中斷之前需要
設(shè)置所需中斷的優(yōu)先級(可選)
使能外設(shè)中的可以觸發(fā)中斷的中斷產(chǎn)生控制
使能NVIC中的中斷
在M4中,中斷優(yōu)先級共8位寬,但芯片廠商可進(jìn)行設(shè)置,范圍是3-8位。中斷優(yōu)先級分為兩個部分,分組優(yōu)先級(也叫搶占優(yōu)先級)和子優(yōu)先級。處理器首先判斷的是分組優(yōu)先級,分組優(yōu)先級高的會被首先處理,若分組優(yōu)先級相同,再比較子優(yōu)先級。8為寬的優(yōu)先級如何分配搶占優(yōu)先級和分組優(yōu)先級,可由寄存器設(shè)置。如下表
優(yōu)先級分組
搶占優(yōu)先級域
分組優(yōu)先級域
0(默認(rèn))
Bit[7:1]
Bit[0]
1
Bit[7:2]
Bit[1:0]
2
Bit[7:3]
Bit[2:0]
3
Bit[7:4]
Bit[3:0]
4
Bit[7:5]
Bit[4:0]
5
Bit[7:6]
Bit[5:0]
6
Bit[7]
Bit[6:0]
7
無
Bit[7:0]
這里要注意的是,異常編號和優(yōu)先級并不是一個意思,異常編號僅僅是一個編號,就像是枚舉一樣,而優(yōu)先級則是需要手動的進(jìn)行設(shè)置的。只有在分組優(yōu)先級和子優(yōu)先級完全一致時,異常編號才起作用,編號越小優(yōu)先級越高。
另外,在M4內(nèi)核中,還提供了中斷向量重定位功能。向量表重定位功能提供了一個名為向量表偏移寄存器(VTOR)的可編程寄存器。前面提到的Bootloder就可以使用此項功能來完成。
5.4 異常或中斷屏蔽寄存器
5.4.1 PRIMASK
PRIMASK用于禁止除NMI和HardFault外的所有異常,只能在特權(quán)狀態(tài)訪問如:
CPSIE I ;清除PRIMASK(使能中斷)
CPSID I;設(shè)置PRIMASK (禁止中斷)
5.4.2 FAULMASK (M0中無)
FAULMASK用于禁止除NMI外的所有異常,只能在特權(quán)訪問,如:
CPSIE F ;清除FAULMASK
COSID F;設(shè)置FAULMASK
5.4.3 BASEPRI(M0中無)
BASEPRI可禁止優(yōu)先級低于某特定等級的中斷,只能在特權(quán)狀態(tài)訪問,如:
_set_BASEPRI(0X60); //禁止優(yōu)先級在0x60-0xFF間的中斷
_set_BASEPRI(0X0); //取消BASEPRI屏蔽
5.5 中斷狀態(tài)及中斷行為
5.5.1 中斷狀態(tài)
中斷狀態(tài):inactive, pending, active,active and pending
中斷狀態(tài)之間的轉(zhuǎn)換:
Inactive(非活躍):沒有掛起或激活的狀態(tài)
Pending(掛起):異常已經(jīng)被觸發(fā)但是處理還未處理。比如當(dāng)前中斷被觸發(fā),但是有高優(yōu)先級或同等優(yōu)先級的中斷正在執(zhí)行,或是中斷使能被禁止。
Active(活躍):中斷正在被處理
Active and pending(活躍并掛起) 異常被處理時,同一個異常再次被觸發(fā)。
上述異常對應(yīng)的是每一個異常狀態(tài),不是整體的異常。
5.5.2 中斷行為
每個中斷都有多個屬性:
每個中斷都可以被禁止或使能
每個中斷都可以被掛起或解除掛起
每個中斷都可以處于活躍或非活躍狀態(tài)
這樣中斷就會產(chǎn)生多種行為,先來看一下最簡單的中斷下中斷狀態(tài)的轉(zhuǎn)換
中斷的掛起狀態(tài)是可以手動設(shè)置的,有一個寄存器是中斷掛起狀態(tài)寄存器,例如上下文切換的PendSV異常,就是手動設(shè)置PendSV掛起,然后進(jìn)入了PendSV異常中。同樣,掛起狀態(tài)是可以清除的。若中斷請求產(chǎn)生時,處理器正在處理更高優(yōu)先級的請求,然后在處理器對該中斷請求作出相應(yīng)之前,掛起狀態(tài)被清除掉了,那么該中斷請求也就不會被處理了。
但是,如果外設(shè)保持某中斷為請求狀態(tài),那樣即使軟件清除掉掛起狀態(tài),掛起狀態(tài)仍會再次置位。
中斷處理完成后,如果中斷請求仍在繼續(xù)保持,中斷就再次進(jìn)入掛起狀態(tài),再次得到處理器的服務(wù)
對于脈沖中斷請求,若在處理器開始處理前,中斷請求產(chǎn)生了多次,它只會被當(dāng)做一次請求
中斷的掛起狀態(tài)可以在其正在被處理時再次置位,也就是之前所說的active and pend狀態(tài)。中斷處理完成后會再次處理該中斷。
另外有一種情況需要注意,即使禁止了某中斷,中斷的掛起狀態(tài)仍然可以使用,只是無法轉(zhuǎn)化為活躍狀態(tài),并且,如果中斷被使能后,若沒有高優(yōu)先級中斷在執(zhí)行,掛起狀態(tài)就會被轉(zhuǎn)換為活躍狀態(tài),進(jìn)而進(jìn)入中斷處理,如果不希望此類狀況發(fā)生,那應(yīng)該在中斷使能之前清除掉中斷掛起狀態(tài)。
5.6 各Cortex-M處理器NVIC差異
Cortex-M處理器的中斷管理有一定區(qū)別
CM0
CM0+
CM1
CM3
CM4
中斷數(shù)量
1-32
1-32
1/8/16/32
1-240
1-240
NMI
Y
Y
Y
Y
Y
優(yōu)先級寬度
2
2
2
3-8
3-8
寄存器訪問
字
字
字
字、半字、字節(jié)
字、半字、字節(jié)
PRIMASK
Y
Y
Y
Y
Y
FAULTMASK
N
N
N
Y
Y
BASEPRI
N
N
N
Y
Y
向量表偏移寄存器
N
Y(可選)
N
Y
Y
動態(tài)修改優(yōu)先級
N
N
N
Y
Y
中斷活躍狀態(tài)
N
N
N
Y
Y
錯誤處理
硬件錯誤
硬件錯誤
硬件錯誤
硬件錯誤+3個其他錯誤異常
硬件錯誤+3個其他錯誤異常
調(diào)試監(jiān)控異常
N
N
N
Y
Y
6 異常處理
6.1 C實現(xiàn)的異常處理
C函數(shù)在ARM架構(gòu)上是如何工作的,用于ARM架構(gòu)的C編譯器遵循ARM的一個名為AAPCS的規(guī)范。根據(jù)這份標(biāo)準(zhǔn),C函數(shù)可以修改R0-R3、R12、R14(LR)以及PSR。若C函數(shù)需要使用R4-R11,就應(yīng)該將這些寄存器保存到??臻g中,并且在函數(shù)結(jié)束前將他們恢復(fù)。
R0-R3、R12、R14、PSR被稱作“調(diào)用者保存寄存器”。
R4-R11為“被調(diào)用者保存寄存器”。
一般來說,函數(shù)調(diào)用將R0-R3作為輸入?yún)?shù),R0則用作返回結(jié)果。若返回值為64位,R1也會用于返回結(jié)果。
異常機制需要在異常入口處自動保存R0-R3、R12、R14、PSR,異常退出時將其恢復(fù),這些都要由處理器硬件控制。另外,與普通的C函數(shù)調(diào)用不同,返回地址PC并沒有存儲在LR中,進(jìn)入異常時LR存儲的是EXC_RETURN,異常返回時將會用到。因此,異常流程也需要將返回地址保存,所以,不算浮點單元,異常處理期間保存的寄存器為8個。對于帶浮點單元的Cortex-M4處理器,異常機制還會保存S0-S15以及FPSCR。
6.2 棧幀
在異常入口處被壓入??臻g的數(shù)據(jù)塊為棧幀。不具有浮點單元的M4處理器,棧幀是8個字大小。帶浮點的棧幀可能是8個或26個字大小。與上一個小節(jié)中所說的異常處理期間保存的寄存器為8個是一個意思。
AAPCS要求棧指針的數(shù)值在函數(shù)入口和出口處應(yīng)是雙字對齊的,雙字棧對齊特性可編程,該特性可以關(guān)閉。M0中不可設(shè)置。壓棧的xPSR的第9位表示棧指針的數(shù)值是否調(diào)整過,0未調(diào)整,1調(diào)整過,也就是會自動插入一個字的空間。所以,棧幀最大為9或27個字。
6.3 EXC_RETURN
6.1小節(jié)中說到過,處理器進(jìn)入異常時,LR存儲的是EXC_RETURN,這里說明一下,EXC_RETURN的具體細(xì)節(jié)。
EXC_RETURN是一個字大小,位域表示如下
位
描述
數(shù)值
31:28
EXC_RETURN 指示
0xF
27:5
保留 全為1
0Xefffff
4
棧幀類型
(M0中無)
1:8字
0:26字
3
返回模式
1:返回線程模式
0:返回處理模式
2
返回棧
1:返回線程棧
0:返回主棧
1
保留
0
0
保留
1
EXC_RETURN的合法值
浮點未使用FPCA=1
浮點使用FPCA=0
返回處理模式,總使用主棧
0XFFFFFFE1
0XFFFFFFF1
返回線程模式,返回后使用主棧
0XFFFFFFE9
0XFFFFFFF1
返回線程模式,返回后使用進(jìn)程棧
0XFFFFFFED
0XFFFFFFFD
__asm void vPortSVCHandler( void )
{
PRESERVE8
ldr r3, =pxCurrentTCB /* Restore the context. */
ldr r1, [r3]
ldr r0, [r1]
ldmia r0!, {r4-r11}
msr psp, r0 /* Restore the task stack pointer. */
isb
mov r0, #0
msr basepri, r0
orr r14, #0xd /*或上0b1101,返回線程棧,線程模式*/
bx r14
}
6.4異常流程
6.4.1 異常進(jìn)入和壓棧
當(dāng)異常產(chǎn)生且被處理器接受時,壓棧流程會將寄存器壓入棧中并組織棧幀。要注意的是,壓棧期間的棧訪問順序和棧幀中的順序不同,首先壓棧的是PC和xPSR,這樣在取向量時會盡快更新PC。
這里再復(fù)習(xí)一下主棧與進(jìn)程棧的使用,在處理模式,必須使用主棧,在進(jìn)程模式,由CONTROL寄存器控制使用主棧還是進(jìn)程。
在處理器處于進(jìn)程模式并且使用進(jìn)程棧時,壓棧操作使用進(jìn)程棧,然后就進(jìn)入了處理模式,直到異常返回之前,一直使用的是主棧,如異常嵌套的情況,在一個異常中又進(jìn)入了另一個異常,仍然使用的是主棧進(jìn)行壓棧。
另外,我們在使用OS的時候會有兩個??臻g,系統(tǒng)棧空間和任務(wù)??臻g,系統(tǒng)??臻g是在啟動文件中進(jìn)行了設(shè)置,如圖6-1,這個??臻g是留給異常使用的,也就是處理模式下的MSP;而在創(chuàng)建任務(wù)的時候都會有對??臻g的設(shè)置,我們稱為任務(wù)??臻g,也就是在線程模式下的PSP。
進(jìn)入異常時,會將R0-R3,R12,LR和返回地址(帶浮點單元時包括S0-S15和FPSCR)硬件自動壓棧保存,其他的(R4-R11)需要軟件保存。
6.4.2 異常返回和出棧
出棧時就是對EXC_RETURN數(shù)值的判斷,也就是EXC_RETURN中位域所表示的那幾項,用什么棧進(jìn)行壓棧的仍返回什么棧,操作模式和棧幀類型也類似。出棧操作結(jié)束時,還要檢查xPSR的第9位,若置1則去除壓棧時插入的額外空間。
對于??臻g中的數(shù)據(jù),同入棧一樣,R0-R3,R12,和LR(帶浮點單元時包括S0-S15和FPSCR)是自動出棧的,其余的需要軟件出棧。
7 低功耗和系統(tǒng)控制特性
7.1 低功耗模式
CORTEX-M系列處理器提供兩種休眠模式:休眠模式和深度休眠模式。由系統(tǒng)控制寄存器SCR控制。
處理器提供了兩個用于進(jìn)入休眠模式的指令:WFI,進(jìn)入休眠模式,等待中斷,可由中斷、調(diào)試、復(fù)位喚醒。WFE:等待事件,條件進(jìn)入休眠模式。內(nèi)部事件寄存器為0,進(jìn)入休眠,否則內(nèi)部事件寄存器被清除,處理器繼續(xù)執(zhí)行,除了中斷、調(diào)試、復(fù)位喚醒外,還能由事件喚醒。
WFE中的事件喚醒包括事件輸入信號脈沖(RXEV)。該信號屬于事件通信接口特性的一部分。處理器還存在一個名為TXEV(發(fā)送事件)的輸出信號,當(dāng)執(zhí)行SEV(發(fā)送事件)指令時,TXEV會輸出一個周期的脈沖信號。
事件通信接口的主要設(shè)計目標(biāo)位,在一個特定事件發(fā)生前讓處理器一直處于休眠模式。例如:
允許外設(shè)和處理器之間的通信
允許多個處理器間的通信
7.1 SysTick定時器
Cortex-M處理器內(nèi)集成了一個小型的名為Systick的定時器,它屬于NVIC的一部分,可以產(chǎn)生Systick異常。Systick為簡單的向下計數(shù)的24位計數(shù)器。
Systick的主要作用是用于在OS中任務(wù)管理和上下文切換,處理器可以在不同時間片內(nèi)處理不同任務(wù)。
設(shè)計這個定時器的目的是為了增加軟件的可移植性。所有CORTEX-M系列的芯片都有相同的定時器。定時器的時鐘可以是處理器時鐘或者是外部參考時鐘(STCLK)。
定時器的使用很簡單,只有四個寄存器
地址
寄存器
作用
0XE000E010
Systick控制和狀態(tài)寄存器
使能以及設(shè)置Systick
0XE000E014
Systick重裝載值寄存器
Systick計時周期
0XE000E018
Systick當(dāng)前值寄存器
Systick當(dāng)前數(shù)值
0XE000E01C
Systick校準(zhǔn)值寄存器
校準(zhǔn)設(shè)置
使用CMSIS-Core中的函數(shù)就能方便地使用Systick,主要作用就是產(chǎn)生定時中斷,如下,產(chǎn)生1HZ的定時中斷
配置正確的始終頻率數(shù),產(chǎn)生1khz的定時中斷
Sys_Config(SystemCoreClock/1000);
SystemCoreClock為對應(yīng)芯片的時鐘頻率
SysTick定時器的寄存器只能在特權(quán)狀態(tài)下訪問。
8 OS支持特性
8.1 OS支持特性簡介
上下文切換是OS中很重要的一點,經(jīng)過前面的學(xué)習(xí),我們知道一個OS的運行就是在不同任務(wù)間頻繁的切換,而在切換時,就需要對上一個任務(wù)的一些數(shù)據(jù)進(jìn)行保存,然后進(jìn)入下一個任務(wù),并取出這個任務(wù)保存的數(shù)據(jù)。可以手動的實現(xiàn)一個系統(tǒng)的上下文切換。
處理器架構(gòu)實現(xiàn)了多個特性,保證了OS設(shè)計的方便和高效:
影子棧指針。有兩個棧指針,MSP用于OS內(nèi)核以及中斷處理,PSP用于應(yīng)用任務(wù)。
Systick定時器。處理器內(nèi)部的簡單的定時器??蓪崿F(xiàn)任務(wù)的定時器切換
SVC和PendSV異常。這兩種異常對于嵌入式OS的操作非常重要,如上下文的切換的實現(xiàn)等等。
非特權(quán)等級執(zhí)行,可以利用其實現(xiàn)一種基本安全模型
8.2 SVC和PendSV
SVC稱作請求管理調(diào)用,它既是一條指令,也是一種異常。SVC指令就可以進(jìn)入SVC異常中,之前說道過從非特權(quán)轉(zhuǎn)換到特權(quán)模式就可以使用SVC異常的方法。并且SVC異常通常是不能被打斷的,如果打斷就會出現(xiàn)硬件錯誤。
PendSV異常是為完成OS的上下文切換,并且PendSV的異常等級要設(shè)置為最低。這是因為如果PendSV的異常等級比較高的話,當(dāng)其他異常來臨或正在執(zhí)行時,上下文切換就會打斷其他異常,上下文的切換較為復(fù)雜,就造成中斷延遲。這在實時系統(tǒng)中是不可以的。所以要把PendSV異常等級設(shè)置為最低,等待所有中斷執(zhí)行完畢后,再進(jìn)行上下文切換。下圖是兩種方式的對比。
任務(wù)切換一般有兩種方式,一種可以通過systick中斷觸發(fā)PendSV異常,比如在時間片式的任務(wù)中;對于手動方式,可以通過SVC觸發(fā)PendSV異常,完成上下文切換。這里要說明一下的是,下面兩幅圖中省略了觸發(fā)PendSV的過程,Systick異常優(yōu)先級是比較高的,它的作用簡單,只是觸發(fā)PendSV異常,它對IRQ的搶占影響很小。
8.3 實際的上下文切換
上下文切換操作由PendSV異常執(zhí)行處理,由于異常流程已經(jīng)保存了寄存器的R0-R3、R12、LR、xPSR和返回地址,PendSV只需將R4-R11保存到棧中。
具有四個任務(wù)的多任務(wù)系統(tǒng)實例:
/*字訪問的宏定義*/
#define HW32_REG(ADDRESS) (*(volatile unsigned long *)ADDRESS)
/*以上宏定義相當(dāng)于
Int *p;
p = (int*)0x60000000;
*p = 0x01;
就是可以對ADDRESS地址處賦值,也就是訪問這個字地址處的數(shù)據(jù)。*/
Void task0(void);
Void task1(void);
Void task2(void);
Void task3(void);
//任務(wù)事件
Volatile uint32_t systick_count=0;
//每個任務(wù)用的棧 8KB
long long task0_stack[1024], task1_stack[1024], task2_stack[1024], task3_stack[1024];
//OS使用的數(shù)據(jù)
uint32_t curr_task = 0; //當(dāng)前任務(wù)
uint32_t next_task = 0; //下一個任務(wù)
uint32_t PSP_array[4]; //任務(wù)的進(jìn)程棧指針
//--------------------------------------------------------------------
// 主程序
int main(void)
{
//任務(wù)初始化 省略
//開始任務(wù)調(diào)度
//創(chuàng)建任務(wù)0棧幀
PSP_array[0] = ((unsigned int) task0_stack) + (sizeof task0_stack) -16*4;
/*初始化函數(shù)地址,14作為PC的位置,左移2位相當(dāng)于乘以4*/
HW32_REG(PSP_array[0] + (14<<2)) = (unsigned long) task0;
/*初始化程序計數(shù)器,15作為PSR位置*/
HW32_REG(PSP_array[0] + (15<<2)) = 0x01000000;
/*初始化其他任務(wù)*/
……..
…….
…….
Curr_task = 0; //切換到任務(wù)0
/*設(shè)置PSP為任務(wù)0的棧頂*/
_set_PSP((PSP_array[curr_task] + 16*4))
NVIC_SetPriority(PendSV_IRQn,0xFF); //設(shè)置PendSV為最低優(yōu)先級
SysTick_Config(16800);
_set_CONTROL(0X3);
_ISB();
task0();
while(1);
{stop_cpu;//不應(yīng)到此處}
}
Void task0();
{
}
Void task1();
{
}
Void task2();
{
}
Void task3();
{
}
_asm void PendSV_Handler()
{
//1保存當(dāng)前上下文
MRS R0,PSP //讀取當(dāng)前棧指針
STMDB R0!,{R4-R11} //將R4-R11保存到任務(wù)棧中
LDR R1, = _cpp(&curr_task)
LDR R2, [R1] //當(dāng)前任務(wù)ID,也就是在PSP_array中的位置
LDR R3, = _cpp(&PSP_array) //PSP_array地址
STR R0,[R3,R2,LSL #2] //當(dāng)前任務(wù)棧PSP的位置存放到PSP_array中,上//文的保存完成
//2 加載下一個上下文
LDR R4 , =_cpp(&next_task)
LDR R4,[R4] //得到下一個任務(wù)的ID,也就是在PSP_array存放的//位置
STR R4,[R1]
LDR R0,[R3,R4,LSL #2] //從PSP_array中取出這個任務(wù)的棧起始地址
LDMIA R0!,{R4-R11} //從任務(wù)棧中取出保存的R4-R11
MSR PSP, R0 //設(shè)置PSP為下一個任務(wù)
BX LR
ALIGN 4
}
Void SysTick_Handler(void)
{
Systick_count++;
//簡單的任務(wù)調(diào)度器
Switch(curr_task)
{
Case 0:
Next_task = 1;break;
Case 1:
Next_task = 2;break;
Case 2:
Next_task = 3;break;
Case 3:
Next_task = 0;break;
Default: next_task = 0;
Stop_cpu;
Break;//不應(yīng)到此處
}
If(curr_task!=next_task)
{
SCB->ICSR |=SCB_ICSR_PENDSVSET_Mask;
}//在這里設(shè)置了PENDSV掛起,在PENDSV_HANDLER中完成上下文切換
Return;
}
對于帶浮點單元的上下文切換,基本流程是一致的。這里要了解帶浮點單元的一種壓棧機制,惰性壓棧。
帶浮點的情況下,處理器進(jìn)入異常時,若要將每個異常所需的浮點單元寄存器壓棧(S0-S15,F(xiàn)PSCR),每次都需要額外執(zhí)行17次存儲器壓棧操作,會增加中斷等待時間。
為減少等待時間,M4處理器實現(xiàn)一種惰性壓棧的特性。默認(rèn)是使能的。若在浮點單元使能且使用的情況下產(chǎn)生了異常,則棧幀的長度會增加。不過,這些浮點寄存器的數(shù)值實際上是不會寫入棧幀中的。惰性壓棧機制只會為這些寄存器保留一定的??臻g,不過只有R0-R3、R12、LR、返回地址和xPSR被壓棧。當(dāng)出現(xiàn)惰性壓棧時,一個名為LSPACT(惰性壓棧保持活躍)的寄存器會被置位,浮點單元上下文地址寄存器(FPCAR)存放浮點寄存器預(yù)留??臻g的地址。
在棧初始化時,由于惰性壓棧的緣故,并不需要單獨考慮浮點單元的空間,但仍需要增加一個字的空間,這個用來保存LR(EXC_RETURN),它的第四位表示異常棧幀中是否包含浮點寄存器。上下文切換時通過檢查它來確認(rèn)是否需要壓入或彈出浮點寄存器的相關(guān)內(nèi)容。
鏈接地址:http://m.appdesigncorp.com/p-6479778.html