und异常

文章目录

    • 1 uboot中的异常向量表
    • 2 und异常处理程序示例
    • 3 分析
    • 4 程序改进

1 uboot中的异常向量表

查看uboot中源码uboot\u-boot-1.1.6\cpu\arm920t,打开start.S:

	/*code: 28 -- 72*/
    #include 
    #include 
    
    
    /*
     *************************************************************************
     *
     * Jump vector table as in table 3.1 in [1]
     *
     *************************************************************************
     */
    #define GSTATUS2   (0x560000B4)
    #define GSTATUS3   (0x560000B8)
    #define GSTATUS4   (0x560000BC)
    
    #define REFRESH(0x48000024)
    #define MISCCR (0x56000080)
    
    #define LOCKTIME	0x4C000000	/* R/W, PLL lock time count register */
    #define MPLLCON		0x4C000004	/* R/W, MPLL configuration register */
    #define UPLLCON		0x4C000008	/* R/W, UPLL configuration register */
    #define CLKCON		0x4C00000C	/* R/W, Clock generator control reg. */
    #define CLKSLOW		0x4C000010	/* R/W, Slow clock control register */
    #define CLKDIVN		0x4C000014	/* R/W, Clock divider control */
    
    /******下面这些就是异常向量表*****/
    .globl _start
    _start:	b   reset
    	ldr	pc, _undefined_instruction
    	ldr	pc, _software_interrupt
    	ldr	pc, _prefetch_abort
    	ldr	pc, _data_abort
    	ldr	pc, _not_used
    	ldr	pc, _irq
    	ldr	pc, _fiq
    
    _undefined_instruction:	.word undefined_instruction
    _software_interrupt:	.word software_interrupt
    _prefetch_abort:	.word prefetch_abort
    _data_abort:		.word data_abort
    _not_used:		.word not_used
    _irq:			.word irq
    _fiq:			.word fiq
    
    	.balignl 16,0xdeadbeef

手册异常向量表定义:
und异常_第1张图片


2 und异常处理程序示例

.text
    .global _start
    
    _start:
    	b reset  /* vector 0 : reset */  //一上电复位,是从0地址开始执行,跳到reset处
    	b do_und /* vector 4 : und */ //如果发生未定义指令异常,就会跳到0x04地址未定义指令异常处,执行do_und程序
    
    /*假设一上电从0地址开始执行,reset,做一系列初始化之后
    *故意加入一条未定义指令
    	
    und_code:
    	.word 0xdeadc0de  //未定义指令
    当CPU发现无法执行此条指令时,就会发生未定义指令异常,就会执行do_und
    	bl print2,
    */
    do_und:
    	/* 执行到这里之前:
    	 * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
    	 * 2. SPSR_und保存有被中断模式的CPSR
    	 * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
    	 * 4. 跳到0x4的地方执行程序 
    	 */
		//需要从新设置sp栈,指向某一块没有使用的地址    
    	/* sp_und未设置, 先设置它 */
    	ldr sp, =0x34000000
    
    	/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
		/* 发生异常时,当前被中断的地址会保存在lr寄存器中 先减后存*/
    	/* lr是异常处理完后的返回地址, 也要保存 */
    	stmdb sp!, {r0-r12, lr}
    	
    	/* 保存现场 */
    	/* 处理und异常 */
    	mrs r0, cpsr//把cpsr的值读入r0
    	ldr r1, =und_string//把下面的字符串地址赋值给r1

    	bl printException
    	
		/* 这些寄存器保存在栈中,把他读取出来就可以了*/
    	/* 恢复现场 */
		/* 先读后加*/
		/* 把r0 ~ r12的值从栈中都取出来,并且把原来保存的lr值,赋值到pc中去*/
    	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */

/*
*如何定义字符串,可以百度搜索 arm-linux-gcc 汇编 定义字符串
*
*官方的说明文档
*http://web.mit.edu/gnu/doc/html/as_7.html
.string "str"

Copy the characters in str to the object file. You may specify more than one string to copy, separated by 
commas. Unless otherwise specified for a particular machine, the assembler marks the end of each string with
 a 0 byte. You can use any of the escape sequences described in section Strings. 

我们使用.str会自动加上结束符
*/ 	
    und_string:
    	.string "undefined instruction exception"
    .align 4	//一定要4字节对齐,否则可能会出错
    
    reset:
    	/* 关闭看门狗 */
    	ldr r0, =0x53000000
    	ldr r1, =0
    	str r1, [r0]
    
    	/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
    	/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
    	ldr r0, =0x4C000000
    	ldr r1, =0xFFFFFFFF
    	str r1, [r0]
    
    	/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
    	ldr r0, =0x4C000014
    	ldr r1, =0x5
    	str r1, [r0]
    
    	/* 设置CPU工作于异步模式 */
    	mrc p15,0,r0,c1,c0,0
    	orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
    	mcr p15,0,r0,c1,c0,0
    
    	/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 
    	 *  m = MDIV+8 = 92+8=100
    	 *  p = PDIV+2 = 1+2 = 3
    	 *  s = SDIV = 1
    	 *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
    	 */
    	ldr r0, =0x4C000004
    	ldr r1, =(92<<12)|(1<<4)|(1<<0)
    	str r1, [r0]
    
    	/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
    	 * 然后CPU工作于新的频率FCLK
    	 */
    	

    	/* 设置内存: sp 栈 */
    	/* 分辨是nor/nand启动
    	 * 写0到0地址, 再读出来
    	 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
    	 * 否则就是nor启动
    	 */
    	mov r1, #0
    	ldr r0, [r1] /* 读出原来的值备份 */
    	str r1, [r1] /* 0->[0] */ 
    	ldr r2, [r1] /* r2=[0] */
    	cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
    	ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
    	moveq sp, #4096  /* nand启动 */
    	streq r0, [r1]   /* 恢复原来的值 */
    
    	bl sdram_init
    	//bl sdram_init2	 /* 用到有初始值的数组, 不是位置无关码 */
    
    	/* 重定位text, rodata, data段整个程序 */
    	bl copy2sdram
    
    	/* 清除BSS段 */
    	bl clean_bss
    
    	bl uart0_init
    
    	bl print1
    	/* 故意加入一条未定义指令 */
    und_code:
    	.word 0xff123456  /* 未定义指令 */
    	bl print2
    
    	//bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
    	ldr pc, =main  /* 绝对跳转, 跳到SDRAM */
    
    halt:
    	b halt

如何处理这个异常呢?
直接print打印一句话,新建一个exception.c文件。

#include "uart.h"
    
void printException(unsigned int cpsr, char *str) //cpsr打印相应的寄存器,str打印一个字符串
{
	puts("Exception! cpsr = ");\\打印cpsr
	printHex(cpsr);//输出cpsr的值
	puts(" ");//输出空格
	puts(str);//输出str值
	puts("\n\r");//回车,换行
}

修改makefile文件:

all: start.o led.o uart.o init.o main.o exception.o
    	#arm-linux-ld -Ttext 0 -Tdata 0x30000000  start.o led.o uart.o init.o main.o -o sdram.elf
    	arm-linux-ld -T sdram.lds $^ -o sdram.elf 
		#用$ ^来包含所有的依赖
    	arm-linux-objcopy -O binary -S sdram.elf sdram.bin
    	arm-linux-objdump -D sdram.elf > sdram.dis
    clean:
    	rm *.bin *.o *.elf *.dis
    	
    %.o : %.c
    	arm-linux-gcc -c -o $@ $<
    
    %.o : %.S
    	arm-linux-gcc -c -o $@ $<
    	*.dis

3 分析

 .text
    .global _start
/*一上电复位,从0地址开始执行
跳到   reset:
做了一系列初始化
当执行到0xdeadc0de这条指令时候,CPU根本就不知道这条指令什么意思
    und_code:
    	.word 0xdeadc0de  //未定义指令 
    	bl print2
让后就发生未定义指令异常,他会把下一条指令的地址保存到异常模式的LR寄存器

    	/* 执行到这里之前已经发生了很多事情
    	 * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
    	 * 2. SPSR_und保存有被中断模式的CPSR
    	 * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
    	 * 4. 跳到0x4的地方执行程序 
    	 * 
    	 * 设置栈 sp是指und的地址
    	 * sp_und未设置, 先设置它
    	 * 	在und异常处理函数中有可能会修改r0-r12, 所以先保存 
    	 * lr是异常处理完后的返回地址, 也要保存 
    	 * 保存现场 
    	 * 处理und异常 
    	 * 恢复sp
    	 * cpu就会切换到之前的模式
    	 */
*/    
.text
.global _start

_start:
	b reset  /* vector 0 : reset */
	b do_und /* vector 4 : und */
    
    und_addr:
    	.word do_und
    
    do_und:
    	/* 执行到这里之前:
    	 * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
    	 * 2. SPSR_und保存有被中断模式的CPSR
    	 * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
    	 * 4. 跳到0x4的地方执行程序 
    	 */
    
    	/* sp_und未设置, 先设置它 */
    	ldr sp, =0x34000000
    
    	/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
    	/* lr是异常处理完后的返回地址, 也要保存 */
    	stmdb sp!, {r0-r12, lr}  
    	
    	/* 保存现场 */
    	/* 处理und异常 */
    	mrs r0, cpsr
    	ldr r1, =und_string
    	bl printException
    	
    	/* 恢复现场 */
    	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
    	
    und_string:
    	.string "undefined instruction exception"

4 程序改进

使得异常处理在SDRAM中进行,做出如下改进:

 .text
    .global _start
    
    _start:
    	b reset  /* vector 0 : reset */
/*跳转到sdram执行这个函数,那么这个函数一定在sdram中
我们需要指定让他去前面这块内存去读这个值,担心如果这个文件很大,超过4Knand就没法去读这个文件*/
    	ldr pc, und_addr /* vector 4 : und */

/*增加如下 查看反汇编,在08的地址读让后跳到3c*/    
    und_addr:
    	.word do_und
    
    do_und:
    	/* 执行到这里之前:
    	 * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
    	 * 2. SPSR_und保存有被中断模式的CPSR
    	 * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
    	 * 4. 跳到0x4的地方执行程序 
    	 */
    
    	/* sp_und未设置, 先设置它 */
    	ldr sp, =0x34000000
    
    	/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
    	/* lr是异常处理完后的返回地址, 也要保存 */
    	stmdb sp!, {r0-r12, lr}  
    	
    	/* 保存现场 */
    	/* 处理und异常 */
    	mrs r0, cpsr
    	ldr r1, =und_string
    	bl printException
    	
    	/* 恢复现场 */
    	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
    	
    und_string:
    	.string "undefined instruction exception"
    /**如果你的程序长度稍有变化,就不能保证运行
	加上 .align 4才能保证后面的程序以4字节对齐,保证程序运行
	**/
    .align 4
    
    reset:
    	/* 关闭看门狗 */
    	ldr r0, =0x53000000
    	ldr r1, =0
    	str r1, [r0]
    
    	/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
    	/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
    	ldr r0, =0x4C000000
    	ldr r1, =0xFFFFFFFF
    	str r1, [r0]
    
    	/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
    	ldr r0, =0x4C000014
    	ldr r1, =0x5
    	str r1, [r0]
    
    	/* 设置CPU工作于异步模式 */
    	mrc p15,0,r0,c1,c0,0
    	orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
    	mcr p15,0,r0,c1,c0,0
    
    	/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 
    	 *  m = MDIV+8 = 92+8=100
    	 *  p = PDIV+2 = 1+2 = 3
    	 *  s = SDIV = 1
    	 *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
    	 */
    	ldr r0, =0x4C000004
    	ldr r1, =(92<<12)|(1<<4)|(1<<0)
    	str r1, [r0]
    
    	/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
    	 * 然后CPU工作于新的频率FCLK
    	 */
    	
    	
    
    	/* 设置内存: sp 栈 */
    	/* 分辨是nor/nand启动
    	 * 写0到0地址, 再读出来
    	 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
    	 * 否则就是nor启动
    	 */
    	mov r1, #0
    	ldr r0, [r1] /* 读出原来的值备份 */
    	str r1, [r1] /* 0->[0] */ 
    	ldr r2, [r1] /* r2=[0] */
    	cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
    	ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
    	moveq sp, #4096  /* nand启动 */
    	streq r0, [r1]   /* 恢复原来的值 */
    
    	bl sdram_init
    	//bl sdram_init2	 /* 用到有初始值的数组, 不是位置无关码 */
    
    	/* 重定位text, rodata, data段整个程序 */
    	bl copy2sdram
    
    	/* 清除BSS段 */
    	bl clean_bss
    
 /*把链接地址赋值给pc 直接就跳转到sdram中*/   
    	ldr pc, =sdram
    sdram:
    	bl uart0_init
    
    	bl print1
    	/* 故意加入一条未定义指令 */
    und_code:
    	.word 0xdeadc0de  /* 未定义指令 */
    	bl print2
    
    	//bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
    	ldr pc, =main  /* 绝对跳转, 跳到SDRAM */
    
    halt:
    	b halt

程序执行流程分析:
und异常_第2张图片

你可能感兴趣的:(Linux)