分类: 嵌入式
2011-09-28 09:08:25
cortex-m3 是一款非常强大的单片机,和arm A系列(应用处理器)的确有很大的区别
但是还是很多相似之处,毕竟都是arm的东西
下面是阅读CM3权威指南的一些摘要:
1. P25 两个级别(user,svc),2个运行模式(handler,thread),3种组合(user没有handler mode)
handler ,thread 的区别可能就是(4)中的自动压栈
特权 | 用户
———————————————————————
异常(exp,irq)处理 handler | 错误
———————————————————————
主应用的代码 线程 | 用户
注意mode 切换图
[特权handler mode]
/ ^ \ ^
/ / \ \
/ / (异常返回) (触发异常)
(异常返回) (触发异常) \ \
/ / \ \________________
/ / \_____________ |
/ / | |
V / V |
复位---> [特权级thread mode]----(修改CONTROL REG)--->(用户级thread mode)
CM3:
handler mode (注意总是 MSP!)
thread mode, 可以使用 MSP
thread mode, 也可以使用PSP,通过control reg 设置
注意和ARM9 ARM A系统等比较
它们在各异常(Abort,Undefine,IRQ,FIQ),User,Supervisor 都有自己的SP(R13)
移值时注意这个巨大差异!!!
2. P27 内存的分布
3. P30 异常名称介绍,[-1]----[-3] 3个固定优先级,NMI[-2] 的作用不错
P43 11-SVCCall, 15-SysTick 可以关注下
4. P32 进入异常服务例程,自动压栈 R0-R3,R12,LR(R14),PSR,PC(R15)
5. P36 栈指针分MSP(SP_main), PSP(SP_process) ,push,pop 指令根据CONTROL[1](P40],自动判断当前那个SP
如果有特殊要求可以通过MRS/MSR 来指定访问
6. P38 注意读PC 时,当前支流+4 ,和 arm9上 (PC+8)有区别,
pc 奇数这句表示必须在thumb2 下
7. P39 特殊功能寄存器 MRS/MSR 读/写
PSR 分:
APSR 应用程序PSR (N,Z,C,V,Q)[31-27]
IPSR 中断号PSR (Exception Number) [8-0]
(如果是thread,那么值为0,因为没有0号异常)
EPSR 执行PSR (ICI/IT)[26:25] ,(T)[34],(ICI/IT)[15:10]
8. P39. 异常(中断) 控制register
PRIMASK 开关中断
FAULTMASK 开关异常
BASEPRI 优先级
9. P40 快速开关中断异常
CPSID I ;PRIMASK=1 关
CPSIE I ; =0 开
CPSID F ;FAULTMASK 关
CPSIE F
10. P40 CONTROL register
CONTROL[1] 0---MSP (reset default) (handler mode 必须为0)
CONTROL[1] 1---PSP
CONTROL[0] 1---svc mode
CONTROL[0] 0---user mode
11. P41 复位后为 thread mode + svc mode
thread mode + svc mode 对系统空间阻止访问,MRS/MSR 也禁用,否则fault
注意user ,svc mode 的切换 (control[0] + user 发生异常(+SVCall)
12. P43 异常相量表
P109 ,16 个异常,240 IRQ
13. P46 使用满递减栈,双栈(svc,user),用于带kernel的 user ,kernel 切换很好
14. P47 奇特的复位过程,可以看下
1. 从0x0地址取MSP
2. 从0x4地址取PC的值,这个值是复位向量,LSB 必须为1,然后从这个地址所
对应的地址处取值
[reset] -->[0x0(MSP)][0x4(复位向量)]----->[Reset Vector] !
(0x0--开始是MSP,然后接下拉是向量表!!!)
(向量表中都是32bit 地址,比如0x4放的就是reset 执行指令位置)
P148 图3.18
15. P50 指令后缀使用:
S 更新APSR中标志 EQ,NE,LT,GT 等等
等只有B(跳转)指令才能任意用,其他指令引入IF-THEN 块,在这个块中才可以加后缀
16. P51 注意thumb 语法和thumb 2 以及arm 的区别 ,在UAL 下thumb2 和arm 基本相似
但也可以用thumb 语法明确 16bit 指令
或者通过 .N(16bit) .W(32bit) 来指定 16bit ,32bit
17. P59 自增自减,根据指令宽度, LDMIA Rd! ,{register list} 读后Rd 自增16bit
LDMIA.W RD! ,{register list} 读后Rd 自增32bit
18. P69 BX reg, 指令中reg 中最低位必须为1 (LSB=1) ,因为CM3 只在Thumb 中允许,否则fault
而BLX reg arm ,thumb 都支持,根据LSB=1 thumb LSB=0 arm
19. P71 大多数16bit 算术逻辑指令,直接更新标志位 ,除 ADD.N Rd,Rn,Rm
32bit 需要S 后缀来控制
20. P72 barrier 指令
DMB --- 数据访问指令等前面数据访问指令完成才执行
DSB --- 任何指令等前面数据访问指令完成才执行
(比如建立完所有向量后追加1条DSB P168)
ISB --- 任何指令等前面指令完成才执行
依次往下表现barrier 更严格
21. P73 带符号 饱和运算指令 SSAT.W Rd #imm5,Rn,{,shift}
不带符号饱和运算指令
22. P75 MRS/MSR 除APSR 可以在user级访问,其他必须特权级
23. P76 IF-THEN(IT)里最多有4条支流
IT
IT
IT
IT
IT指令的意义:
当执行到跳转指令时,需要清洗流水线,处理器必须从跳转地址重新取指,
IT可以改善一些小跳转!!!
24. P76 比较跳转CBZ
并且不会更新标志位
25. P80 跳转表指令TBB ,TBH
26. P84 CM3 存储映射
0x0-------------------------------------------------------------------------4G
|512M(代码)|512M(片上SRAM)|512M(片上设备)|512M(片外SRAM)|512M(片外设备)|512M|
/ |
---------------------------------- |
|内部私有外设总线|外部私有外设总线|其他|
0xE0000000 0xE0040000 0xE0100000
27. P85 CM3 存储器访问属性
Bufferable, Cacheable,Executable,Sharable
28. P88 位带操作, 0bit --- 0x0(位带别名区偏移) 1bit ---- 0x4
操作 位带区 bit2
无别名 使用别名
LDR R0,=0x20000000 LDR R0,=0x22000008
LDR R1,[R0] MOV R1,#1
ORR.W R1, #0x4
STR R1,[R0] STR R1,[R0]
注意位带别名地址必须对齐到字, 位带别名操作原子的
29. P94 对齐的地址传输:
LDR/STR, LDM/STM, PUSH/POP, LDREX/STREX,位带别名操作
30. P96 LDREX/STREX 实现mutex 的原理
LDREX后,处理器会在内部标记出一段地址(范围可定义),后续执行到STR/STREX,
只要其存储地址落在标记范围内,就会清除此标记,
下一个STREX 执行先检查有没有做过标记和范围,否则驳回STREX
31. P113 子优先级至少1bit,所以抢占优先级最多7bit,这就造成了128级抢占情况
P112 AIRCR(中断及复位控制寄存器) 0xE000_ED00
其中[10:8]PRIGROUP 设置优先分组
比如写5,表示从第5bit 处分,前面2bit表示抢占优先级(P171)
一般3级:
3bit =>0x00,0x20,0x40,0x60,0x80,0xA0,0xC0,0xE0
这8个值有效
32. P116 向量表偏移寄存器(VTOR:0xE000_ED08)
向量数对齐到2的整次幂,比如 32irq+16except=48 =>64 个
然后64*4 =256,那么按0x100 对起的偏移量
(P170 向量表移动的sample!)
33. P117 当某中断的服务例程开始执行时,就称此中断进入了active 状态,并且其悬起位
会被硬件自动清除 ,一般ISR 执行完成,才能对新请求响应,所以软件在ISR中
清除中断请求
34. P121 总线fault 状态寄存器(BFSR) 0xE000ED29
35. P122 MemManage fault 的原因:
NVIC 中寄存器:
MFSR(指出导致MemManage fault 的原因)
DACCVIOL bit --- 数据访问违例
DACCVIOL bit --- 取指访问违例
MMAR---可读出引发此fault 的地址
在不带mmu 的操作系统中,这个东西具体发挥什么先进功能???
36. P122 用法fault必须被使能才能正常响应,使能位是NVIC的系统handler控制及状态register
的USGFAULTENA,
总线fault 和 MemManage fault 则不需要enable
用法fault 的原因可以查 UFSR(用法fault状态寄存器) 0xE000_ED2A
37. P124 硬fault状态寄存器 0xE000_ED2C
发生的三种情况: 调试, 其他fault 上访,取向量(异常处理时对向量表读取)
注意各fault服务例程在处理后要清FSRs状态寄存器
38. P125 如果分kernel user,那么user svc 进入kernel,如果user 使用PSP ,那么
kernel 访问PSP (MRS Rn,PSP) 找到user 的栈指针,然后分析LR
39. P127 关于PSVC的应用!!!
OS在某中断活跃时尝试切入,线程模式,将触发use fault
40. P129 除软件中断寄存器外,所有中断控制及状态寄存器只能在特权级下访问
41. P130 enable disable 中断:(写1有效)
SETENA0---SETENA7 0xE000_E100---E11C
CLRENA0-7 0xE000_E180---E19C
可以参考P168的sample! (REG addr offset = (N/32)*4)
42. P131 中断pending :
SETPEND0-7 (写1悬起) 0xE000_E200---E21C
CLRPEND0-7 (写1解悬) 0xE000_E280---E29C
43. P131 中断优先级寄存器 (8bit可分为高底两段,分别为抢占优先级和亚优先级)
PRI_0---239 (每个8bit)(0xE000_E400-E4EF)
及系统异常优先级寄存器(0xE000_ED18-ED23)
44. P132 中断活动状态 只读,处理器执行了ISR第1条指令后set1,
直到ISR返回时才硬件清0
ACTIVE0---7 (0xE000_E300---E31C)
45. P133 PRIMASK disable NMI,硬fault 之外所有异常(将当前优先级改为0)
关中断 MOV R0,#1
MSR PRIMASK,R0
或者 CPSID i
开中断 MOV R0,#0
MSR PRIMASK,R0
或者 CPSIE i
FAULTMASK 连硬fault 也disable (将当前优先级改为-1)
但注意异常退出时自动清0!
BASEPRI 寄存器 用于mask 低于某优先级的中断
BASEPRI_MAX 只能越设越高(只能扩大mask 范围)
用户态不能访问BASEPRI
根据优先级位数判断mask值是否有效
3bit =>0x00,0x20,0x40,0x60,0x80,0xA0,0xC0,0xE0
这8个值有效
通过动态计算的方法在P167
46. P134 系统handler控制及状态寄存器SHCSR(0xE000_ED24)
使用这部分寄存器请小心
一般在: 系统调用过程中执行上下文切换
使用软件模拟为定义指令功能
47. P135 中断控制及状态寄存器ICSR (0xE000_ED04)
VCETPENDING 悬起的最高优先级的ISR的编号
RETTOBAE ???
VECTACTIVE 当前活动的ISR编号
P137 中断系统设置过程示例
48. P137 软件触发中断寄存器STIR(0xE000_EF00)
写入8,则悬起IRQ #8
(P170) 软件中断的使用说明 (P137通过软件触发某 IRQ n)
1. 设置NVIC USERSETMPEND位
2. 写0xE000EF000后必须执行DSB(数据同步隔离)
3. 该IRQ n必须使能
4. 如果只是为了系统调用尽量用SVC 指令
49. P138 SysTick 控制及状态寄存器(0xE000_E010)
SysTick reload寄存器(0xE000_E014)
SysTick 当前数值寄存器(0xE000_E010)
SysTick 校准数值寄存器(0xE000_E010)
注意当处理器在调试时被halt,SytTick也暂停运行!
50. P141 响应异常的3个动作(!!!):
1.入栈
xPSR,PC,LR,R12,R3-R0
当前上PSP 就压PSP,否则压MSP,进入服务例程一直使用MSP
P142 如果栈有双字对齐,如果不对齐,先空4字节,然后压栈
2.取向量
3.更新寄存器
51. P143 异常返回(!!!)及指令
BX LR (EXC_RETURN)
POP {...,PC}
LDR/LDM =>PC
1.出栈, 2.更新NVIC
(同级异常不可重入)
52. P144 咬尾中断(硬件处理)
当处理器响应异常时,有发生异常,前次异常push的,仍旧对下次异常有效
P145 晚到中断的方式基本同上, 省去一个pop,push
53. P147 异常返回值
LR=
0xFFFF_FFF1 返回handler mode (注意总是MSP!)
0xFFFF_FFF9 返回thread mode,并使用MSP
0xFFFF_FFFD 返回thread mode,并使用PSP
正真的LR ,已经在主程被打断前自动入栈了,(LR=>EXC_RETURN,只是指出在那个SP!!!)
0xFFFF_FFF1所指主程其实就是被抢占的服务例程
54. P148 质量LDM/STM 的中止和继续(xPSR开出若干ICI 位)
55. P148 异常响应时的faults
1.入栈 => 总线fault BFSR bit4 STKERR
存储fault MFSR bit4 MSTKERR
2.出栈 => 总线fault BFSR bit3 UNSTKERR
存储fault MFSR bit3 UNMSTKERR
3.取向量 => 硬fault HFSR bit VECTTBL
4. 无效返回 =>use fault => UFSR bit2 INVPC ,bit1 INVSTATE
(if disabled then hard fault)
56. P152. 在使用C开发时建议开启CM3的双字对齐
#define NVIC_CCR ((volatile unsigned long *)(0xE000ED14))
*NVIC_CCR = *NVIC_CCR |0x200; //set STKALIGN
57. P155 一个串口输出程序
P160 使用mutex 实现信号量访问
LDREX R2,[R1]
...
STREX R2,R0, [R1]
CMP R2,#0 => R2 == 0 那么就表示成功
P161 使用位带实现互斥锁操作 (P88=> 位带别名操作原子的)
P163 使用位段提取与查表跳转
58. P165 主堆栈安全容量计算方法
每个中断都可以嵌套,对于每1级至少需要8字(32字节),附加考虑ISR局部变量的大小量
进程堆栈处了满足本进程所有函数调用的最大需求量外,好要保留8字,用于容纳第1级
中断时被保护的寄存器(然后进入主栈)
59. P167 RBIT R2,R1, 反转R1,并LSB对齐 R1=0xE0 =>R2=0x07
(P55表) CLZ R1,R2 计算前导0个数 R1=0x07 =>R2=0x5
60. P169 一个ISR 汇编的主结构 (注意R4-R11要自己保护, 其他8个自动保护P141)
PUSH {R4-R11,LR} ... POP {R4-R11,PC} 浪费了1字LR再压栈1次
PUSH {R4-R11} ... POP {R4-R11},BX LR 更节约
61. P171 一个比较完整的中断处理例程
其中SetupIrqHandler 负责建立向量表中断服务例程入口地址,所以向量表
必须移动到RAM中,否则无法动态设置!!!
62. P173 SVC使用:
1.SVC 提供1个8位立即数
2.SVC框架:
svc_handler
TST LR, #4 ;测试EXC_RETURN的bit2
ITE EQ ; 如果为0,下面包括2条指令 (P76)
MRSEQ R0,MSP ; 如果==0 则使用MSP
MRSNE R0,PSP ; 否则使用PSP
LDR R1,[R0,#24] ;从栈中读取PC值 (+24见P142表9.1)
;注意自动压栈8个reg,
;总是8字对齐的所以不用考虑
LDRB R0,[R1,#-2] ;从立既数取系统调用号
; P54表4.5 SVC 是16bit 指令
;准备系统调用func,这需要适当调整入栈的PC及LR(EXC_RETRUN),
来进入OS内部
BX LR ;借异常返回形式,进入OS 内部,call sysfunc
;可使用TBB/TBH查表跳转加速
;返回user的情况如何处理 ???
63. P173 一个具体的SVC 例程
(也可以放在Rx,linuxEABI-R7) 但注意咬尾+晚到情况(P176)!!!
P178 一个gcc 内联汇编的svc 代码
64. P180 一个MPU系统的布局
__________________________________________________________
|用户数据 | 用户栈 | 特权数据 | 特权栈 |
----------------------------------------------------------
(user 级访问) ^ (supervisor级访问) ^
PSP MSP
65. P180 一个简单OS的init 和返回user级的过程
(这里叫user级,不叫user空间,因为没有MMU)
1. OS init
2. 建立PSP,并创建任务的堆栈(PC,xPSR)
3. 把EXC_RETURN 改为0xFFFFFFFD 返回 (P147)
66. P181 一个使用Systick及PendSV(优先级最低异常)来实现轮转调度和上下文切换的例子
1. user程序#1 ===> 响应 Systick 异常后进入OS
2. OS 执行系统管理
3. OS 悬起PendSV以便从systick
异常返回后执行调度和switch context
4. 从systick返回如果无其他异常或中断则进入
PendSV ,这时通过PSP 保存user#1程序
堆栈帧,再把PSP 指向user#2程序的堆栈帧
修改EXC_RETURN,(#2堆栈帧pop2psp)并且返回
67. P182 非基级thread mode的使用
可以在服务例程中切换到thread mode,这样如果中断服务例程是用户程序的1部分
,可能需要让它在thread mode 下执行,以现在访问特权级下的资源
比如:
redirect_handler
PUSH {LR}
SVC #0) ;通过系统服务,由特权级进入user级
(BL User_IRQ_Handler )
(SVC #1 ;执行完中断处理后,回特权级
POS {PC} ;本次中断返回
上面带括号中的指令,包括SVC #0 返回部分,和SVC #1的进入部分
,都是在PSP+thread mode 下运行,理解这部分有助手动调栈的了解
* 所以第1次调整PSP,是为了SVC0返回用,否则svc0不知道返回那里
因为SVC0是在PSP上返回,所以将SVC0的MSP上内容cpy过来
* 第2次调整为了SVC1的返回,因为SVC1压栈在PSP,返回在MSP
所以,先将PSP上+0x18的内容读出(pop pc指令地址),然后
修改MSP上原来SVC0压入的LR(+0x18),然后PSP东西抛弃
然后Bx LR,返回到SVC1下面那条指令
* 一般情况下异常是在Handler+MSP下,但是通过
1. 对PSP中已经压入的IPSR清0
2. 然后NVIC中配置和控制寄存器CCR的NONBASETHRDENA位设置
3. 最后修改EXC_RETURN,并BX LR返回,就可以返回到PSP+thread
特殊的一半在handler+MSP一半在thread+PSP
需要手动调栈具体见P183-184:
主要就是 SVC #0 时特权级IRQ栈(MSP)上的内容到PSP上,
然后进入user thread,注意进入user thread要清0,IPSR
而这步必须在特权级下做!!!
68. P185 1. 消灭存储器等待周期的方法 ???
2. 中断向量尽量放代码区,防止取向量与入栈抢总线
3. 限制非对齐访问
一些汇编的技巧:
1.使用带偏移的LDR/STR,可以省去地址增减等计算
2. 上下文相关的变量放到一起,这样就可以创造使用LDM/STM
连续地址的数据传送
3. 当遇到很小if then ,可以使用(ITxxx),最多包括4条
4. 尽量使用thumb-2 指令
5. 多用CM3新指令
69. P186 锁定图12.5
70. P191 消除systick 异常两次悬起,通过在异常处理中除能systick,并清除
NVIC的Systick 悬起位(0xE000ED04)
71. P192 系统控制寄存器(0xE000_ED10)
bit 4 SEVONPEND
bit 2 SLEEPDEEP 当进入睡眠mode时,使能外部的SLEEPDEEP
信号,以允许停止系统时钟
bit 1 SLEEPONEXT
图13.2进入睡眠模式的序列
WFI(WaitForInterrupt) WFE(WaitForEvent)
| |
| |
| (清除事件锁存器) 事件锁存器!=1 |
<-------------------------<------|
| 事件锁存器==1
SLEEPDEEP!=1) | (SLEEPDEEP==1) |
|---------------| V
| | (清除事件锁存器)
V V (然后执行下条指令)
进入普通睡眠(idle) 进入深度睡眠(suspend)
SLEEPING信号为高 SLEEPING和SLEEPDEEP都为高
SLEEPDEEP为低!
72. P193 唤醒小节:
P194 SleepOnExit 自动睡眠功能演示
73. P194 多机通信功能 (有机会再看)
74. P202 CM3共支持8个regions,允许把每个region进1步划分为更小的 子region
次外还允许启用1个背景region(即没有MPU时的全部地址空间)
75. P204 PRIVDEFENA =1 允许特权级访问背景region (图14.1)
注意有个列外: 不管MPU如何限制,响应异常是的取向量操作,以及对系统分区(E000_xxxx)
的访问不受影响
76. P205 1. MPURNR (MPU region 号) 0xE000ED98
2. MPURBAR (基地址) 0xE000ED9c
[31:N] N >=16 ADDR
[4] valid
[3:0] 那个region
3. MPURASR (属性及容量) 0xE000EDA0
当region 不够用,如果是特权级就放到背景region中
总之这款arm 单片机就其单片机的角色,的确强大,特别是MPU,user级特权级等概念一看就是跑RTOS的好材料,但是感觉象ucos这样简单的RTOS 不是很合适,但比较强大的也不行,比如linux,主要是因为MMU,虽然都有支持linux 的cortex m3的patch ,但没有了MMU,也没发挥出强大os的特性,不过也没见过那款单片机带MMU的,所以个人认为cortex m3 应该有一款自己特定的os ,才能最大发挥他的特点