中级会员
- 积分
- 254
- 金钱
- 254
- 注册时间
- 2020-3-22
- 在线时间
- 35 小时
|
本帖最后由 YOKI 于 2020-4-1 16:20 编辑
/********************************************/
时间 :2020/03/31/
作者 :YOKI
导师 :正点原子 左忠凯
硬件 :正点原子 ALPHA I.MX LINUX 开发板
/*******************************************/
本讲 15.1-15.5 编写一个通过按键 KEY0 使用 UART_CTS 的 的中断代码
通过这个历程来学习 IMX6ULL 的中断系统。
重要知识点:
ARM Cortex-A7 构架 提供了 16个 32位 通用寄存器( R0-R15)和2个 程序状态寄存器
各寄存器的含义:
R0-R12 通用寄存器
R13 SP SP指针
R14 LR 连接寄存器 (IRQ模式下) 存放 子函数的返回地址
R15 PC 程序计数器 将 LR 的值 传递给 PC 可实现程序跳转
CPSR 当前程序状态寄存器
SPSR 备份程序状态寄存器
1、复位中断函数
复位中断函数是系统在上电 及复位后执行的第一个函数,包含了对系统进行初始化(系统的运行模式设置,中断向量表设置,中断偏移等)。
这些工作到要在 start.s 中进行。
2、中断服务的编写:
学习过 51 或者 STM32 的人 都知道 中断函数 是在 当前程序运行过程中,由中断事件(Event)触发后进入一段临时运行的程序,
这段程序执行完成后要回到中断发生前的程序运行状态继续执行原来的程序。这就要求,从当前程序进入到中断服务函数后首先要进行
的一件事是 保护现场 :保存当前程序运行的状态或数据 即保存CPU中相关寄存器的数据
本讲 15.1-15.5 编写一个通过按键 KEY0 使用 UART_CTS 的 的中断代码
1.1 中断向量表设置
_start:
/*****************************************/
/*中断向量表
/****************************************/
ldr pc, =Reset_Handler @ 复位中断服务函数
ldr pc, =Undefined_Handler @ 未定义指令中断服务函数
ldr pc, =SVC_Handler @ SVC 特权模式
ldr pc, =Pre_Abort_Hanlder @ 指令预取中止
ldr pc, =Data_Abort_Handler @ 数据中止
ldr pc, =NotUsed_Handler @ 未使用
ldr pc, =IRQ_Interrupt_Handler @ IRQ 外部中断
ldr pc, =FIQ_Interrupt_Handler @ FIQ 快速中断
/****************************************/
为什么要按照以上的顺序写,是因为厂商在在 ARM A7 中 对中断向量表进行了定义:
某个中断触发会默认跳转到对应的中断服务函数的入口地址
地址 中断 描述
0X00 Reset 复位中断
0X04 Undefined 未定义指令中断
0X08 SVC 特权模式
0X0C Pre_Abort 指令预取中止
0X10 Data_Abort 数据中止
0X14 NotUsed 未使用
0X18 IRQ 外部中断
0X1C FIQ 快速中断
@ 可以看到中断函数的地址出现在内存的最开始,所以要写在的 _start 的最前面
2 复位中断函数:
知识点:
@ CP15协处理器 指令
@ MRC : 将 CP15协处理器 的寄存器数据 写到 ARM 寄存器
@ MCR : 将 ARM 处理器 的寄存器数据 写到 CP15 寄存器
@ MRC : 读 CP15 寄存器 MCR 写 CP15 寄存器
@ 使用格式:
@ MCR{cond},<opc1>,<Rt>,<CRn>,<CRm>,<opc12>
@ cond :指令执行的条件码 (若为空则为无条件执行)
@ <opc1>:协处理器要执行的操作码
@ <Rt>: ARM 源/目标 寄存器
@ <CRn>:CP15协处理器的 目标 寄存器
@ <CRm>:CP15协处理器 的 附加目标寄存器(若不需要附加则写c0,否则结果不可预知)
@ <opc12>: 可选的特定操作码,不需要时写 0
MCR{cond},<opc1>,<Rt>,<CRn>,<CRm>,<opc12>
MRC p15, 0, r0, c1, c0, 0
@ 从CP15的 C1寄存器 内的值 写到 ARM的R0寄存器 中
@ 含义 写SCTRL 寄存器(系统控制寄存器)
@ Cortex-A7 Technical ReferenceManua.pdf P59 P138
@ SCTLR 寄存器 部分bit含义
@ bit0: 开启/关闭 MMU 写 1 关闭
@ bit1: 开启/关闭 对齐 写 1 关闭
@ bit2: 开启/关闭 D Cache 写 1 关闭
@ bit11: 开启/关闭 分支预测 写 1 关闭
@ bit12: 开启/关闭 I Cache 写 1 关闭
@ Cortex-A7 Technical ReferenceManua.pdf P141
@ MMU 虚拟内存管理单元
@ D Cache 数据高速缓存
@ I Cache 指令高速缓存
@ Cache 是高速缓存
@ 从容量来说 CPU < 寄存器 < Cache < DDR (内存片)
@ 从速度来说 CPU > 寄存器 > Cache > DDR
@ 这一部分涉及到计算机的相关知识,简单说一下
@ CPU的在处理数据上来说速度是最快的,但是它的容量有限,
@ 有自己装过电脑的都知道 DDR 是内存的意思(严格来说叫 DDR SDRAM)既然有个RAM 大家就知道这是个 随机存储器
@ CPU 容量有限 所以指令和数据都存在 DDR 里面,但是 DDR 对 CPU 而言速度太慢了,所以中间加了一个高速缓存 Cache
@ 按照 数据 和 指令分开的原则分成了 数据高速缓存(D Cache)和指令高速缓存(I Cache)
@ 至于复位后为什么要关闭 MMU 和 I/D Cache 需要大神来解答一下了(我查了一下说是为了提高效率)
@ 暂且先记住这是 系统复位 的一个过程
2复位中断函数编写
Reset_Handler:
@ 使用 CP15协处理器 指令 操作修改 SCTLR 寄存器 ,采用 读——改——写 的方式
MRC p15,0,r0,c1,c0,0 @ 读取 SCTLR 的数据到 R0寄存器 (CP15协处理器)
bic r0,#(1 << 12) @ 关闭 I Cache (bic 清零指令)
bic r0,#(1 << 11) @ 关闭 分支预测
bic r0,#(1 << 2) @ 关闭 D Cache
bic r0,#(1 << 1) @ 关闭 对齐
bic r0,#(1 << 0) @ 关闭 MMU
MCR p15,0,r0,c1,c0,0 @ 将 R0寄存器 的数据写入 SCTLR (CP15协处理器)
@ 这一段就是使用 CP15协处理器指令对 SCTLR 进行修改了
@ 先将 SCTLR 的数据到 R0寄存器
@ 然后 对相关位 写1 关闭 功能
@ 最后 将 R0寄存器 的数据写入 SCTLR
接下来 设置中断向量偏移
ldr r0,=0x87800000 @ 向 r0 寄存器 写入 新的中断向量地址
dsb @ 数据同步隔离
isb @ 指令同步隔离 等待前面的指令执行完毕
MRC p15,0,r0,c1,c0,0 @ 将 r0 中的地址写入寄存器 VBAR=0X87800000
dsb @ 数据同步隔离
isb @ 指令同步隔离 等待前面的指令执行完毕
@ 为什么要设置中断向量偏移?
@ 前面说过 ARM的指令从内存 0X00开始执行
地址 中断 描述
0X00 Reset 复位中断
0X04 Undefined 未定义指令中断
0X08 SVC 特权模式中断
0X0C Pre_Abort 指令预取中止
0X10 Data_Abort 数据中止
0X14 NotUsed 未使用
0X18 IRQ 外部中断
0X1C FIQ 快速中断
@ 可以看到从 0X00 开始到 0X1C 这些地址是 中断向量的入口
@ 但是_start: 从 0X87800000 开始(可以查看前面的 反汇编 xxx.bis 文件)
@ 87800000 <_START>:
@ 那么就需要通过 设置中断向量偏移 的方法 对这个地址偏差进行矫正
@ 中断向量偏移 设置完成后接下来就是 清除BSS段 (略)和各个运行模式下 SP指针的设置
@ 设置处理器进入IRQ 模式
mrs r0, cpsr @ 读取 CPSR 数据到 R0
bic r0, r0, #0X1F @ 清除 R0 的低5位 bit4-bit0 :写入二进制数据(1 1111)
orr r0, r0, #0X12 @ 设置 R0 的低5位 bit4-bit0 :按 位 或 (1 0010)即设置为IRQ 模式
msr cpsr, r0 @ 将 R0 中数据传回 CPSR 寄存器
ldr sp, =0X80600000 @设置 IRQ 模式下 sp 指针
@ 参考:《正点原子I.MX6U Linux 嵌入式驱动开发指南 》第6章 Cortex-A7 MPCore构架 P258 表6.3.2.2
@ cpsr的低5位 [bit4-bit0]
0X10000 User 用户模式
0X10001 FIQ 快速中断模式
0X10010 IRQ 一般中断中断模式
0X10011 SVC SVC超级管理员 特权模式
0X10110 Monitor MON 监控模式
0X10111 Abort ABT 中止模式
0X11010 Hyp HYP模式(超级监听模式)
0X11011 Undefin 未定义
0X11111 Systerm SYS 模式
一般把SVC模式放到最后,各个运行模式下 SP指针的设置完成之后跳转到 C语言 执行
/**********************************************************************************************/
个人总结: 复位中断函数的编写
复位中断函数是系统在上电 及复位后执行的第一个函数,包含了对系统进行初始化(系统的运行模式设置,中断向量表设置,中断偏移等)。
这些工作到要在 start.s 中进行,
1、在 _start: 的开头 加载中断向量表
2、编写复位中断函数
2.1、通过CP15协处理器的有关指令 设置 SCTRL 寄存器(系统控制寄存器)来关闭 MMU 和 I/D Cache
2.2、设置中断向量偏移
2.3、设置各运行模式下的 SP指针
2.4、进入 SVC 特权模式
2.5、跳转到 C 语言函数
为了防止复位过程中 其他中断的影响可以在 复位中断服务函数开始前 关闭 IRQ 等复位工作完成后再开启 IRQ
这一篇是补了 复位函数 的一篇帖子,关于 MMU 和I/D Cache 的部分知识自己查了一些资料,但是还是遗留了一个问题:
为什么在复位时要关闭 MMU和I/D Cache ?
从查到的资料看这些设备都是为了提高系统运行速度而设置的,为什么要在复位中断中将这些设备关闭?
以上注释和代码都是在学习 《正点原子 linux 第二期 裸机开发视频 P29~P31 第15.1-15.3讲 系统中断 》 过程中
跟着 左萌主 学习并加入了一点点自己的思考写成的,作为一个小菜鸡,我大着胆子发出来跟大家一起记录学习
如果有帮助请大家自行参考、下载,如果 转载 请注明出处,并在论坛和我联系。如果有错误请大神指正!左萌主赛高!
/*********************************************************************************************/
作者 :YOKI
导师 :正点原子 左忠凯 (请允许我叫你一声导师吧)
硬件 :正点原子 ALPHA I.MX LINUX 开发板
/*********************************************************************************************/
|
|