ARM-Cortex A9异常处理流程

第一部分

一. 异常的概念

处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生

    这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件

    异常事件处理完成之后再返回到被异常打断的点继续执行程序

二. 异常处理机制

异常处理机制

    不同的处理器对异常的处理的流程大体相似,但是不同的处理器在具体实现的机制上有所不同;比如处理器遇到哪些事件认为是异常事件遇到异常事件之后处理器有哪些动作、处理器如何跳转到异常处理程序如何处理异常、处理完异常之后又如何返回到被打断的程序继续执行等我们将这些细节的实现称为处理器的异常处理机制

三. ARM异常源

概念

    导致异常产生的事件称为异常源

 ARM异常源

    FIQ                                快速中断请求引脚有效                

    IRQ                                外部中断请求引脚有效

    Reset                        复位电平有效

    Software Interrupt        执行swi指令

    Data Abort                        数据终止

    Prefetch Abort                指令预取终止

    Undefined Instruction        遇到不能处理的指令

四. ARM异常模式

异常模式

    在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切

换成对应的异常模式

五. ARM异常响应

ARM产生异常后的动作(自动完成)

    1.拷贝CPSR中的内容到对应异常模式下的SPSR_<mode>

    2.修改CPSR的值

2.1.修改中断禁止位禁止相应的中断

2.2.修改模式位进入相应的异常模式     产生异常

            2.3.修改状态位进入ARM状态

    3.保存返回地址到对应异常模式下的LR_<mode>

    4.设置PC为相应的异常向量(异常向量表对应的地址)

六. 异常向量表

异常向量表

    > 异常向量表的本质是内存中的一段代码

    > 表中为每个异常源分配了四个字节的存储空间

    > 遇到异常后处理器自动将PC修改为对应的地址

    > 因为异常向量表空间有限一般我们不会再这里

      写异常处理程序,而是在对应的位置写一条跳

      转指令使其跳转到指定的异常处理程序的入口

    注:ARM的异常向量表的基地址默认在0x00地址

        但可以通过配置协处理器来修改其地址

七. 异常返回

 ARM异常返回的动作(自己编写)

    1.将SPSR_<mode>的值复制给CPSR

      使处理器恢复之前的状态

    2.将LR_<mode>的值复制给PC

      使程序跳转回被打断的地址继续执行

八. IRQ异常举例

注:整个过程CPSR保存的永远是当前程序运行状态 , SPSR只是异常时对原来的CPSR进行备份

CPSR:当前程序状态寄存器



第二部分:中断代码执行流程分析

一. 启动代码的分析

1. 实验思路

2. 程序源码

2. 1启动文件
.text
.global _start
_start:
	/*
	 * Vector table  异常向量表
	 */ 
	b reset
	b .
	b .
	b .
	b .
	b .
	/*
	 * 从异常向量表再跳转到IRQ的异常处理程序
	 */
	b irq_handler
	b .

reset:
	/*
	 * Set vector address in CP15 VBAR register
	 */ 
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR

	/*
	 * Set the cpu to SVC32 mode, Disable FIQ/IRQ
	 */  
	mrs r0, cpsr
	bic r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr ,r0

	/*
	 * Defines access permissions for each coprocessor
	 */  
    mov	r0, #0xfffffff
    mcr	p15, 0, r0, c1, c0, 2  	

	/*
	 * Invalidate L1 I/D                                                                                                                   
	 */
	mov	r0, #0					@Set up for MCR
	mcr	p15, 0, r0, c8, c7, 0	@Invalidate TLBs
	mcr	p15, 0, r0, c7, c5, 0	@Invalidate icache
	
	/*
	 * Set the FPEXC EN bit to enable the FPU
	 */ 
	mov r3, #0x40000000
	fmxr FPEXC, r3
	
	/*
	 * Disable MMU stuff and caches
	 */
	mrc	p15, 0, r0, c1, c0, 0
	bic	r0, r0, #0x00002000		@Clear bits 13 (--V-)
	bic	r0, r0, #0x00000007		@Clear bits 2:0 (-CAM)
	orr	r0, r0, #0x00001000		@Set bit 12 (---I) Icache
	orr	r0, r0, #0x00000002		@Set bit 1 (--A-) Align
	orr	r0, r0, #0x00000800		@Set bit 11 (Z---) BTB
	mcr	p15, 0, r0, c1, c0, 0

	/*
	 * Initialize stacks                                                                                                                  
	 */
init_stack:     
	/*svc mode stack*/
	msr cpsr, #0xd3
	ldr sp, _stack_svc_end

	/*undef mode stack*/
	msr cpsr, #0xdb
	ldr sp, _stack_und_end

	/*abort mode stack*/	
	msr cpsr,#0xd7
	ldr sp,_stack_abt_end

	/*irq mode stack*/	
	msr cpsr,#0xd2
	ldr sp, _stack_irq_end
	
	/*fiq mode stack*/
	msr cpsr,#0xd1
	ldr sp, _stack_fiq_end
	
	/*user mode stack, enable FIQ/IRQ*/
	msr cpsr,#0x10
	ldr sp, _stack_usr_end

	/*Call main*/
	b main

	/*
	 * IRQ的异常处理程序
	 */
irq_handler:
	/*
	 * 因为产生IRQ异常后ARM自动保存到LR中的返回地址是被IRQ打断的指令
     * 的下一条再下一条指令的地址,所以我们需要人为的去修正一下
	 */
	sub lr, lr, #4
	/*
	 * 因为IRQ模式下使用的R0-R12寄存器和USER模式下使用的是同一组
	 * 所以在处理异常之前需要先将之前寄存器中的值压栈保护
	 */
	stmfd sp!, {r0-r12,lr}
	/*
	 * 跳转到do_irq处理异常
	 */
	bl do_irq
	/*
	 * 异常返回
	 * 1.将R0-R12寄存器中的值出栈,使其恢复到被异常打断之前的值
	 * 2.将SPSR寄存器中的值恢复到CPSR,使CPU的状态恢复到被异常打断之前
	 * 3.将栈中保存的LR寄存器的值出栈给PC,使程序跳转回被异常打断的点继续执行
	 */
	ldmfd sp!,{r0-r12,pc}^  // '^'意思是自动将SPSR中的值赋值给

_stack_svc_end:      
	.word stack_svc + 512
_stack_und_end:      
	.word stack_und + 512
_stack_abt_end:      
	.word stack_abt + 512
_stack_irq_end:      
    .word stack_irq + 512
_stack_fiq_end:
    .word stack_fiq + 512
_stack_usr_end:      
    .word stack_usr + 512

.data
stack_svc:      
	.space 512
stack_und:
	.space 512
stack_abt:      
	.space 512
stack_irq:      
	.space 512
stack_fiq:      
	.space 512
stack_usr:      
	.space 512
 2.2用户文件
#include "exynos_4412.h"

unsigned int Flags = 0;

void Delay(unsigned int Time)
{
	while(Time--);
}

//IRQ异常处理
void do_irq(void)
{
	unsigned int IrqNum = 0;
	/*从中断控制器中获取当前中断的中断号*/
	IrqNum = CPU0.ICCIAR & 0x3FF;
	
	/*根据中断号处理不同的中断*/
	switch(IrqNum)
	{
		case 0:
			//0号中断的处理程序
			break;
		case 1:
			//1号中断的处理程序
			break;
			/*
			 * ... ...
			 */
		case 58:    //[EINT 10]
			Delay(1000000);
			Flags++;
			printf("key3 pressed.\n");
			/*清除GPIO控制器中GPX1_2的中断挂起标志位*/
			EXT_INT41_PEND = (1 << 2);
			/*将当前中断的中断号写回到中断控制器中,以这种方式来告知中断控制器当前的中断已经处理完成,可以发送其它中断*/
			CPU0.ICCEOIR = CPU0.ICCEOIR & (~(0x3FF)) | (58);
			break;
			/*
			 * ... ...
			 */
		case 159:
			//159号中断的处理程序
			break;
		default:
			break;
		
	}

}

void LED2_ON() {
	GPX2.DAT = GPX2.DAT | (1 << 7);
}

void LED2_OFF() {
	GPX2.DAT = GPX2.DAT & (~(1 << 7));
}

int main()
{

	/******************************/
	//初始化
	/******************************/
	/*外设层次 - 让外部的硬件控制器产生一个中断信号发送给中断控制器*/
	/*将GPX1_1设置成中断功能*/
	GPX1.CON = GPX1.CON | (0xF << 8);
	/*设置GPX1_1的中断触发方式为下降沿触发*/
	EXT_INT41_CON = EXT_INT41_CON & (~(0x7 << 8)) | (0x2 << 8);
	
	/*使能GPX1_2的中断功能*/
	EXT_INT41_MASK = EXT_INT41_MASK & (~(1 << 2));



	/******************************/
	//中断控制器层次
	/******************************/
	/*中断控制器层次 - 让中断控制器接收外设产生的中断信号并对其进行管理然后再转发给CPU处理*/
	/*全局使能中断控制器使其能接收外设产生的中断信号并转发到CPU接口*/
	ICDDCR = ICDDCR | 1;

	/*在中断控制器中使能57号中断,使中断控制器接收到57号中断后能将其转发到CPU接口*/
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 26);
	/*选择由CPU0来处理57号中断*/
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xFF << 16)) | (0X01 << 16);

	/*使能中断控制器和CPU0之间的接口,使中断控制器转发的中断信号能够到达CPU0*/
	CPU0.ICCICR = CPU0.ICCICR | 1;

	/*初始化GPX2_7为输出功能 为LED2做准备*/
	GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);

    while(1)
	{   
		switch(Flags % 2) {
		case 0:
			LED2_ON();
			printf("LED2_ON\n");
			Delay(1000000);
			break;
		case 1:
			LED2_OFF();
			printf("LED2_OFF\n");
			Delay(1000000);
			break;
		}
	}   

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@daiwei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值