ARM处理器的未定义指令异常处理过程分析

在前面的两篇文章中已经介绍了ARM处理器的工作模式和ARM异常中断处理流程。这篇文章我们通过代码来详细介绍ARM处理器未定义指令的异常中断处理;当发生未定义指令异常中断时,CPU进入未定义指令模式。可以通过读取CPSR寄存器的值来判定是否真的进入了未定义指令模式。

开发板:tiny4412;

工具链版本:gcc version 4.5.1 (ctng-1.8.1-FA)

主要设置以下几个文件:

start.S文件,详细内容如下:

.text
.global _start
_start:
	b reset									/* vector 0x46000000 reset*/
	ldr	pc, _undefined_instruction			/* vector 0x46000004 Undefined instruction*/
	/* 我们将程序重定义到0x46000000地址.并在后面设置CP15协处理器将异常向量表地址设置为0x46000000
	 * 所以,发生复位异常时,程序跳转到0x46000000地址开始执行.发生未定义指令异常时,跳转到0x46000004
	 * 开始执行.
	*/

_undefined_instruction: 
	.word undefined_instruction

/* 如果发生未定义指令异常,则程序跳转到这里执行 */
undefined_instruction:
	/* 注意:程序走到这里,说明已经发生了未定义指令异常;
	 * CPU已经完成了以下工作:
	 * 1.lr_und寄存器保存有被中断模式中的下一条即将执行的指令的地址;
	 * 2.SPSR_und保存有被中断模式的CPSR寄存器的值;
	 * 3.CPSR中的M4-M0被设置为11011, 进入到und模式;
	 * 4.跳到0x46000004的地址处开始处理异常. 
	*/

	/* 由于处理异常调用了C函数.所以,设置异常模式下使用的栈:sp_undef */
	ldr sp, =0x48000000
	/* 将在其它模式下可能使用的寄存器r0-r12入栈保存,并将lr中的值也入栈 */
	stmdb sp!, {r0-r12, lr}

	/* 处理异常
	 * R0和R1来传输参数
	*/
	mrs r0, cpsr
	ldr r1, =undef_string
	/* 跳转到C函数中执行,打印CPSR中的值和字符串 */
	bl printException	

	/* 返回现场;从栈中将r0-r12原来的值取回;并将原来lr中的值保存pc中;^符合表示将SPSR中的值返回到CPSR中. */
	ldmia sp!, {r0-r12, pc}^

undef_string:	//定义一个字符串
	.string "Undefined Instruction Exception!"

.align 4		//这里一定要4个字节对齐

/* 复位异常入口 */
reset:
	/* 重新设置异常向量表的地址 */
	ldr	r0,=0x46000000
	mcr	p15,0,r0,c12,c0,0		// Vector Base Address Register

	ldr sp, =0x02027400			//调用C函数之前必须设置栈,栈用于保存运行环境,给局部变量分配空间;
								//参考ROM手册P14,我们把栈指向BL2的最上方;
								//即:0x02020000(iROM基地址)+5K(iROM代码用)+8K(BL1用)+16K(BL2用)

	/* 首先,初始化时钟 */
	bl system_clock_init

	/* 初始化内存控制器,使LPDDR可以使用.然后就可以将程序重定位到LPDRR中执行 */
	bl mem_ctrl_asm_init

	//重定位整个代码到0x4200_0000地址处;这个地址位于LPDDR里面
	adr r0, _start			//将_start标号(程序的开始地址)位于iRAM里的实际地址保存到R0寄存器中;也是开始拷贝程序的地址

	ldr r1, =_start			//获取链接地址;也就是想将程序运行的地址;将拷贝的程序从这个地址开始保存

	ldr r2, =bss_start		//将链接地址中的bss_start标识地址保存到R1寄存器中;也是拷贝程序结束的地址;R1-R2的大小也就是要
							//重定位代码的大小

	cmp r1, r2				//比较两个地址是否相等,如果相等在直接去清除bss段即可;
	beq clear_bss			//跳转到清零bss处

reload_loop:
	ldr r3, [r0], #4		//将R0数字表示地址处的数据加载到R3寄存器中;并将R0+4
	str r3, [r1], #4		//将R3中的数据保存到R1寄存器数字表示的地址中,并将R1+4
	cmp r1, r2
	bne reload_loop			//循环拷贝

clear_bss:		//将bss段清零
	ldr r0, =bss_start		//将bss段的开始地址保存到R0寄存器
	ldr r1, =bss_end		//将bss段的结束地址保存到R1寄存器
	mov r2, #0x0			//将0数字保存到R2寄存器中
	cmp r0, r1
	beq run_main

clear_loop:
	str r2, [r0], #4		//将0保存到R0寄存器地址,并将R0+4
	cmp r0, r1
	bne	clear_loop			//如果不相等则表示没有清零完成

run_main:
	/* 跳转到LPDDR中执行 */
	ldr pc, =lpddr

lpddr:
	/* 初始化串口 */
	bl uart0_init

	/* 打印调试信息 */
	bl printHello

	/* 专门设置一个未定义指令;执行到这条指令时会跳转到未定义指令异常向量地址处指令 */
	.word 0xdeadc0de  

	/* 点亮LED灯 */
	ldr sp, =0x45000000		//重新设置栈
	mov r0, pc
	ldr pc, =main			//跳转到C函数中执行

halt_loop:					//死循环
	b halt_loop

此文件开始开机运行的第一个文件,里面已经做了详细的注释这里就不再做详细的介绍。

clock_init_tiny4412.S文件主要用来设置时钟,内容如下:

//初始化时钟要使用的宏:
#define ELFIN_CLOCK_BASE		0x10030000
#define CLK_SRC_DMC_OFFSET		0x10200
#define CLK_SRC_CPU_OFFSET		0x14200
#define CLK_DIV_DMC0_OFFSET		0x10500
#define CLK_DIV_DMC1_OFFSET		0x10504
#define CLK_SRC_TOP0_OFFSET		0x0C210
#define CLK_SRC_TOP1_OFFSET		0x0C214
#define CLK_DIV_TOP_OFFSET		0x0C510
#define CLK_SRC_LEFTBUS_OFFSET		0x04200
#define CLK_DIV_LEFTBUS_OFFSET		0x04500
#define CLK_SRC_RIGHTBUS_OFFSET		0x08200
#define CLK_DIV_RIGHTBUS_OFFSET		0x08500
#define APLL_LOCK_OFFSET			0x14000
#define MPLL_LOCK_OFFSET			0x14008
#define EPLL_LOCK_OFFSET		0x0C010
#define VPLL_LOCK_OFFSET		0x0C020
#define CLK_DIV_CPU0_OFFSET		0x14500
#define CLK_DIV_CPU1_OFFSET		0x14504
#define APLL_CON1_OFFSET		0x14104
#define APLL_CON0_OFFSET		0x14100
#define MPLL_CON0_OFFSET		0x10108
#define MPLL_CON1_OFFSET		0x1010C
#define EPLL_CON2_OFFSET		0x0C118
#define EPLL_CON1_OFFSET		0x0C114
#define EPLL_CON0_OFFSET		0x0C110
#define VPLL_CON2_OFFSET		0x0C128
#define VPLL_CON1_OFFSET		0x0C124
#define VPLL_CON0_OFFSET		0x0C120

//使用到的值定义
/* CLK_DIV_DMC0	*/
#define CORE_TIMERS_RATIO	0x0
#define COPY2_RATIO			0x0
#define DMCP_RATIO			0x1
#define DMCD_RATIO			0x1
#define DMC_RATIO			0x1
#define DPHY_RATIO			0x1
#define ACP_PCLK_RATIO		0x1
#define ACP_RATIO			0x3

#define CLK_DIV_DMC0_VAL	((CORE_TIMERS_RATIO << 28) | (COPY2_RATIO << 24) | (DMCP_RATIO << 20) | (DMCD_RATIO << 16)	\
							| (DMC_RATIO << 12)	| (DPHY_RATIO << 8)	| (ACP_PCLK_RATIO << 4)	| (ACP_RATIO))

#define CLK_DIV_DMC1_VAL	0x07071713

/* CLK_SRC_TOP0	*/
#define MUX_ONENAND_SEL 	0x0 	/* 0 = DOUT133, 1 = DOUT166		*/
#define MUX_ACLK_133_SEL	0x0		/* 0 = SCLKMPLL, 1 = SCLKAPLL	*/
#define MUX_ACLK_160_SEL	0x0
#define MUX_ACLK_100_SEL	0x0
#define MUX_ACLK_200_SEL	0x0
#define MUX_VPLL_SEL		0x1
#define MUX_EPLL_SEL		0x1

#define CLK_SRC_TOP0_VAL	((MUX_ONENAND_SEL << 28)|(MUX_ACLK_133_SEL << 24)|(MUX_ACLK_160_SEL << 20)	\
							| (MUX_ACLK_100_SEL << 16)|(MUX_ACLK_200_SEL << 12)|(MUX_VPLL_SEL << 8)	\
							| (MUX_EPLL_SEL << 4))

#define CLK_SRC_TOP1_VAL	(0x01111000)

/* CLK_DIV_TOP	*/
#define ACLK_400_MCUISP_RATIO	0x1
#define ACLK_266_GPS_RATIO	0x2
#define ONENAND_RATIO	0x1
#define ACLK_133_RATIO	0x5
#define ACLK_160_RATIO	0x4
#define ACLK_100_RATIO	0x7
#define ACLK_200_RATIO	0x4

#define CLK_DIV_TOP_VAL		((ACLK_400_MCUISP_RATIO << 24)|(ACLK_266_GPS_RATIO << 20)|(ONENAND_RATIO << 16) \
							| (ACLK_133_RATIO << 12)|(ACLK_160_RATIO << 8)|(ACLK_100_RATIO << 4) \
							| (ACLK_200_RATIO))

/* CLK_SRC_LEFTBUS	*/
#define CLK_SRC_LEFTBUS_VAL	(0x10)

/* CLK_DIV_LEFRBUS	*/
#define GPL_RATIO	0x1
#define GDL_RATIO	0x3
#define CLK_DIV_LEFRBUS_VAL		((GPL_RATIO << 4) | (GDL_RATIO))

/* CLK_SRC_RIGHTBUS	*/
#define CLK_SRC_RIGHTBUS_VAL	(0x10)

/* CLK_DIV_RIGHTBUS	*/
#define GPR_RATIO	0x1
#define GDR_RATIO	0x3
#define CLK_DIV_RIGHTBUS_VAL	((GPR_RATIO << 4) | (GDR_RATIO))

#define APLL_MDIV		0xAF
#define APLL_PDIV		0x3
#define APLL_SDIV		0x0

#define APLL_RATIO		0x2
#define CORE_RATIO		0x0
#define CORE2_RATIO		0x0
#define COREM0_RATIO	0x3
#define COREM1_RATIO	0x7
#define PERIPH_RATIO	0x7
#define ATB_RATIO		0x6
#define PCLK_DBG_RATIO	0x1

/* CLK_DIV_CPU1	*/
#define CORES_RATIO		0x5
#define HPM_RATIO		0x0
#define COPY_RATIO		0x6

#define MPLL_MDIV		0x64
#define MPLL_PDIV		0x3
#define MPLL_SDIV		0x0

#define EPLL_MDIV		0x40
#define EPLL_PDIV		0x2
#define EPLL_SDIV		0x3

#define VPLL_MDIV		0x48
#define VPLL_PDIV		0x2
#define VPLL_SDIV		0x3

/* APLL_LOCK	*/
#define APLL_LOCK_VAL	(APLL_PDIV * 270)

/* MPLL_LOCK	*/
#define MPLL_LOCK_VAL	(MPLL_PDIV * 270)

/* EPLL_LOCK	*/
#define EPLL_LOCK_VAL	(EPLL_PDIV * 3000)

/* VPLL_LOCK	*/
#define VPLL_LOCK_VAL	(VPLL_PDIV * 3000)

#define CLK_DIV_CPU0_VAL	((CORE2_RATIO << 28)|(APLL_RATIO << 24)|(PCLK_DBG_RATIO << 20)|(ATB_RATIO << 16) \
                            |(PERIPH_RATIO <<12)|(COREM1_RATIO << 8)|(COREM0_RATIO << 4)|(CORE_RATIO))

#define CLK_DIV_CPU1_VAL	((CORES_RATIO << 8)|(HPM_RATIO << 4)|(COPY_RATIO))

#define APLL_CON1_VAL		(0x00803800)

/* Set PLL */
#define set_pll(mdiv, pdiv, sdiv)	(1<<31 | mdiv<<16 | pdiv<<8 | sdiv)
#define APLL_CON0_VAL	set_pll(APLL_MDIV,APLL_PDIV,APLL_SDIV)

/* MPLL_CON1	*/
#define MPLL_CON1_VAL 	(0x00803800)
#define MPLL_CON0_VAL	set_pll(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)

#define EPLL_CON2_VAL	0x00000080
#define EPLL_CON1_VAL	0x66010000

#define EPLL_CON0_VAL	set_pll(EPLL_MDIV,EPLL_PDIV,EPLL_SDIV)
#define VPLL_CON2_VAL	0x00000080
#define VPLL_CON1_VAL	0x66010000

#define VPLL_CON0_VAL	set_pll(VPLL_MDIV,VPLL_PDIV,VPLL_SDIV)

/*
 * system_clock_init: Initialize core clock and bus clock. 初始化系统时钟
 * void system_clock_init(void)  时钟初始化,参考uboot中设置的
*/

	.globl system_clock_init
system_clock_init:
	push {lr}

    //往(CLK_SRC_CPU)0x10044200寄存器写入0;
	ldr	r0, =ELFIN_CLOCK_BASE	        @0x1003_0000
	ldr	r1, =0x0
	ldr	r2, =CLK_SRC_CPU_OFFSET         @0x14200
	str	r1, [r0, r2]

	/* 休息 ?us */
	mov	r1, #0x10000
1:	subs	r1, r1, #1
	bne	1b

    //设置CLK_DIV_DMC0(0x10040500)寄存器
	ldr	r1, =CLK_DIV_DMC0_VAL
	ldr	r2, =CLK_DIV_DMC0_OFFSET
	str	r1, [r0, r2]

    //设置CLK_DIV_DMC1(0x10040504)寄存器
	ldr	r1, =CLK_DIV_DMC1_VAL
	ldr	r2, =CLK_DIV_DMC1_OFFSET
	str	r1, [r0, r2]

    
    //设置CLK_SRC_TOP0(0x1003C210)寄存器
	ldr	r1, =CLK_SRC_TOP0_VAL
	ldr	r2, =CLK_SRC_TOP0_OFFSET
	str	r1, [r0, r2]
	//设置CLK_SRC_TOP1(0x1003C214)寄存器
	ldr	r1, =CLK_SRC_TOP1_VAL
	ldr	r2, =CLK_SRC_TOP1_OFFSET
	str	r1, [r0, r2]

	/* 休息 ?us */
	mov	r1, #0x10000
3:	subs	r1, r1, #1
	bne	3b

    //设置CLK_DIV_TOP(0x1003C510)寄存器
	ldr	r1, =CLK_DIV_TOP_VAL
	ldr	r2, =CLK_DIV_TOP_OFFSET
	str	r1, [r0, r2]

    //设置 CLK_SRC_LEFTBUS(0x10034200)寄存器
	ldr	r1, =CLK_SRC_LEFTBUS_VAL
	ldr	r2, =CLK_SRC_LEFTBUS_OFFSET
	str	r1, [r0, r2]

	/* 休息 ?us */
	mov	r1, #0x10000
4:	subs	r1, r1, #1
	bne	4b

    //设置CLK_DIV_LEFTBUS(0x10034500)寄存器
	ldr	r1, =CLK_DIV_LEFRBUS_VAL
	ldr	r2, =CLK_DIV_LEFTBUS_OFFSET
	str	r1, [r0, r2]

    //设置CLK_SRC_RIGHTBUS(0x10038200)寄存器
	ldr	r1, =CLK_SRC_RIGHTBUS_VAL
	ldr	r2, =CLK_SRC_RIGHTBUS_OFFSET
	str	r1, [r0, r2]

	/* 休息 ?us */
	mov	r1, #0x10000
5:	subs	r1, r1, #1
	bne	5b

    //设置CLK_DIV_RIGHTBUS(0x10038500)寄存器
	ldr	r1, =CLK_DIV_RIGHTBUS_VAL
	ldr	r2, =CLK_DIV_RIGHTBUS_OFFSET
	str	r1, [r0, r2]

    //设置APLL_LOCK(0x10044000)寄存器
	ldr	r1, =APLL_LOCK_VAL
	ldr	r2, =APLL_LOCK_OFFSET
	str	r1, [r0, r2]

    //设置MPLL_LOCK(0x10044004)寄存器
	ldr	r1, =MPLL_LOCK_VAL
	ldr	r2, =MPLL_LOCK_OFFSET
	str	r1, [r0, r2]
    //设置EPLL_LOCK(0x1003C010)寄存器
	ldr	r1, =EPLL_LOCK_VAL
	ldr	r2, =EPLL_LOCK_OFFSET
	str	r1, [r0, r2]

    //设置VPLL_LOCK(0x1003C020)寄存器
	ldr	r1, =VPLL_LOCK_VAL
	ldr	r2, =VPLL_LOCK_OFFSET
	str	r1, [r0, r2]

    //设置CLK_DIV_CPU0(0x10044500)寄存器
	ldr	r1, =CLK_DIV_CPU0_VAL
	ldr	r2, =CLK_DIV_CPU0_OFFSET
	str	r1, [r0, r2]

    //设置CLK_DIV_CPU1(0x10044504)寄存器
	ldr	r1, =CLK_DIV_CPU1_VAL
	ldr	r2, =CLK_DIV_CPU1_OFFSET
	str	r1, [r0, r2]

    //设置APLL_CON1(0x10044104)寄存器
	ldr	r1, =APLL_CON1_VAL
	ldr	r2, =APLL_CON1_OFFSET
	str	r1, [r0, r2]
	
	//设置APLL_CON0(0x10044100)寄存器
	ldr	r1, =APLL_CON0_VAL
	ldr	r2, =APLL_CON0_OFFSET
	str	r1, [r0, r2]

	/* check MPLL and if MPLL is not 400 Mhz skip MPLL resetting for C2C operation */
	ldr	r2, =MPLL_CON0_OFFSET
	ldr r1, [r0, r2]
	ldr r3, =0xA0640301
	cmp r1, r3
	bne skip_mpll

    @ Set MPLL
	ldr	r1, =MPLL_CON1_VAL
	ldr	r2, =MPLL_CON1_OFFSET
	str	r1, [r0, r2]
	ldr	r1, =MPLL_CON0_VAL
	ldr	r2, =MPLL_CON0_OFFSET
	str	r1, [r0, r2]

skip_mpll:

    @ Set EPLL
	ldr     r1, =EPLL_CON2_VAL
	ldr     r2, =EPLL_CON2_OFFSET
	str     r1, [r0, r2]
	ldr     r1, =EPLL_CON1_VAL
	ldr     r2, =EPLL_CON1_OFFSET
	str     r1, [r0, r2]
	ldr     r1, =EPLL_CON0_VAL
	ldr     r2, =EPLL_CON0_OFFSET
	str     r1, [r0, r2]

    @ Set VPLL
	ldr     r1, =VPLL_CON2_VAL
	ldr     r2, =VPLL_CON2_OFFSET
	str     r1, [r0, r2]
	ldr     r1, =VPLL_CON1_VAL
	ldr     r2, =VPLL_CON1_OFFSET
	str     r1, [r0, r2]
	ldr     r1, =VPLL_CON0_VAL
	ldr     r2, =VPLL_CON0_OFFSET
	str     r1, [r0, r2]

	/* wait ?us */
	mov	r1, #0x40000
6:	subs	r1, r1, #1
	bne	6b

	ldr	r1, =0x01000001
	ldr	r2, =CLK_SRC_CPU_OFFSET
	str	r1, [r0, r2]
	ldr	r1, =0x00011000
	ldr	r2, =CLK_SRC_DMC_OFFSET
	str	r1, [r0, r2]
	ldr	r1, =0x00000110
	ldr	r2, =CLK_SRC_TOP0_OFFSET
	str	r1, [r0, r2]
	ldr	r1, =0x01111000
	ldr	r2, =CLK_SRC_TOP1_OFFSET
	str	r1, [r0, r2]

	/* wait ?us */
	mov	r1, #0x10000
7:	subs	r1, r1, #1
	bne	7b
	
	pop	{pc}

关于Exynos 4412 CPU时钟的设置前面已经介绍过了,这里不再介绍。

mem_init_tiny4412.S文件主要是设置内存控制器,这里涉及到LPDDR3相关的很多知识点,这里不做详细介绍。

#define APB_DMC_0_BASE			0x10600000 
#define APB_DMC_1_BASE			0x10610000 

#define DMC_PHYCONTROL1 		0x1C
#define DMC_PHYZQCONTROL 		0x44
#define DMC_PHYCONTROL0 		0x18
#define DMC_PHYCONTROL2 		0x20
#define DMC_CONCONTROL 			0x00
#define DMC_MEMCONTROL 			0x04
#define DMC_MEMCONFIG0 			0x08
#define DMC_MEMCONFIG1 			0x0C
#define CONFIG_IV_SIZE 			0x1F
#define DMC_IVCONTROL	 		0xF0
#define DMC_PRECHCONFIG 		0x14
#define DMC_TIMINGAREF 			0x30
#define DMC_TIMINGROW 			0x34
#define DMC_TIMINGDATA 			0x38
#define DMC_TIMINGPOWER 		0x3C
#define DMC_DIRECTCMD 			0x10
#define MCLK_400

	.globl mem_ctrl_asm_init
mem_ctrl_asm_init:

	/* Async bridge configuration at CPU_core:
	 * 1: half_sync
	 * 0: full_sync */
	ldr	r0, =0x10010350
	mov	r1, #1
	str	r1, [r0]


/*****************************************************************/
/*DREX0***********************************************************/
/*****************************************************************/

	ldr	r0, =APB_DMC_0_BASE

	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0xE3854C03
	str	r1, [r0, #DMC_PHYZQCONTROL]

	mov	r2, #0x100000
1:	subs	r2, r2, #1
	bne	1b

	ldr	r1, =0xe000008e
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0x71101008
	str	r1, [r0, #DMC_PHYCONTROL0]
	ldr	r1, =0x7110100A
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0x7110100B
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00000000
	str	r1, [r0, #DMC_PHYCONTROL2]

	ldr	r1, =0x0FFF301A
	str	r1, [r0, #DMC_CONCONTROL]
	ldr	r1, =0x00302640
	str	r1, [r0, #DMC_MEMCONTROL]

	ldr	r1, =0x40c01333
	str	r1, [r0, #DMC_MEMCONFIG0]
	ldr	r1, =0x80e01323
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =(0x80000000 | CONFIG_IV_SIZE)

	str	r1, [r0, #DMC_IVCONTROL]

	ldr	r1, =0xff000000
	str	r1, [r0, #DMC_PRECHCONFIG]

	ldr	r1, =0x000000BB
	str	r1, [r0, #DMC_TIMINGAREF] 	@TimingAref

	ldr	r1, =0x7a46654f
	str	r1, [r0, #DMC_TIMINGROW] @TimingRow
	ldr	r1, =0x46400506
	str	r1, [r0, #DMC_TIMINGDATA] @TimingData
	ldr	r1, =0x52000a3c
	str	r1, [r0, #DMC_TIMINGPOWER] @TimingPower

	/* chip 0 */
	ldr	r1, =0x07000000
	str	r1, [r0, #DMC_DIRECTCMD]

	mov	r2, #0x100000
2:	subs	r2, r2, #1
	bne	2b

	ldr	r1, =0x00020000
	str	r1, [r0, #DMC_DIRECTCMD]
	ldr	r1, =0x00030000
	str	r1, [r0, #DMC_DIRECTCMD]
	ldr	r1, =0x00010002
	str	r1, [r0, #DMC_DIRECTCMD]
	ldr	r1, =0x00000328
	str	r1, [r0, #DMC_DIRECTCMD]

	mov	r2, #0x100000
3:	subs	r2, r2, #1
	bne	3b

	ldr	r1, =0x0a000000
	str	r1, [r0, #DMC_DIRECTCMD]

	mov	r2, #0x100000
4:	subs	r2, r2, #1
	bne	4b

	ldr	r1, =0xe000008e
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]

	mov	r2, #0x100000
8:	subs	r2, r2, #1
	bne	8b


/*****************************************************************/
/*DREX1***********************************************************/
/*****************************************************************/

	ldr	r0, =APB_DMC_1_BASE

	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0xE3854C03
	str	r1, [r0, #DMC_PHYZQCONTROL]

	mov	r2, #0x100000
1:	subs	r2, r2, #1
	bne	1b

	ldr	r1, =0xe000008e
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0x71101008
	str	r1, [r0, #DMC_PHYCONTROL0]
	ldr	r1, =0x7110100A
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0x7110100B
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00000000
	str	r1, [r0, #DMC_PHYCONTROL2]

	ldr	r1, =0x0FFF301A
	str	r1, [r0, #DMC_CONCONTROL]
	ldr	r1, =0x00302640
	str	r1, [r0, #DMC_MEMCONTROL]

	ldr	r1, =0x40c01333			@Interleaved?
	str	r1, [r0, #DMC_MEMCONFIG0]
	ldr	r1, =0x80e01323
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =(0x80000000 | CONFIG_IV_SIZE)
	str	r1, [r0, #DMC_IVCONTROL]

	ldr	r1, =0xff000000
	str	r1, [r0, #DMC_PRECHCONFIG]

	ldr	r1, =0x000000BB
	str	r1, [r0, #DMC_TIMINGAREF] @TimingAref

	ldr	r1, =0x7a46654f
	str	r1, [r0, #DMC_TIMINGROW] @TimingRow
	ldr	r1, =0x46400506
	str	r1, [r0, #DMC_TIMINGDATA] @TimingData
	ldr	r1, =0x52000a3c
	str	r1, [r0, #DMC_TIMINGPOWER] @TimingPower

	/* chip 0 */
	ldr	r1, =0x07000000
	str	r1, [r0, #DMC_DIRECTCMD]

	mov	r2, #0x100000
2:	subs	r2, r2, #1
	bne	2b

	ldr	r1, =0x00020000
	str	r1, [r0, #DMC_DIRECTCMD]
	ldr	r1, =0x00030000
	str	r1, [r0, #DMC_DIRECTCMD]
	ldr	r1, =0x00010002
	str	r1, [r0, #DMC_DIRECTCMD]
	ldr	r1, =0x00000328
	str	r1, [r0, #DMC_DIRECTCMD]

	mov	r2, #0x100000
3:	subs	r2, r2, #1
	bne	3b

	ldr	r1, =0x0a000000
	str	r1, [r0, #DMC_DIRECTCMD]

	mov	r2, #0x100000
4:	subs	r2, r2, #1
	bne	4b

	ldr	r1, =0xe000008e
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0xe0000086
	str	r1, [r0, #DMC_PHYCONTROL1]

	mov	r2, #0x100000
8:	subs	r2, r2, #1
	bne	8b


/*****************************************************************/
/*Finalize********************************************************/
/*****************************************************************/

	ldr	r0, =APB_DMC_0_BASE
	ldr	r1, =0x0FFF303A
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r0, =APB_DMC_1_BASE
	ldr	r1, =0x0FFF303A
	str	r1, [r0, #DMC_CONCONTROL]

	mov	pc, lr

exception.c文件的内容主要是打印异常相关的数据,内容如下:

#include "uart.h"

void printException(unsigned int cpsr, char *str)
{
	/* 打印字符串 */
	puts("Exception cpsr = ");
	/* 打印CPSR中的值 */
	puthex(cpsr);
	/* 打印传入的字符串 */
	puts(str);
	/* 打印换行 */
	puts("\n\r");
}

uart.c文件主要是设置串口相关的东西,前面的文章中已经讲述过,这里不再介绍。内容如下:

//串口0使用的引脚
#define GPA0CON 		(*(volatile unsigned int *)0x11400000)
//选择时钟源
#define CLK_SRC_PERIL0 	(*(volatile unsigned int *)0x1003C250)
//设置uart0的分频系数
#define CLK_DIV_PERIL0 	(*(volatile unsigned int *)0x1003C550)
#define UBRDIV0 		(*(volatile unsigned int *)0x13800028)
#define UFRACVAL0 		(*(volatile unsigned int *)0x1380002C)
#define UFCON0 			(*(volatile unsigned int *)0x13800008)
#define ULCON0 			(*(volatile unsigned int *)0x13800000)
#define UCON0 			(*(volatile unsigned int *)0x13800004)
#define UTXH0 			(*(volatile unsigned int *)0x13800020)
#define URXH0 			(*(volatile unsigned int *)0x13800024)
#define UTRSTAT0 		(*(volatile unsigned int *)0x13800010)

void uart0_init()
{	
	/* 1.设置相应的GPIO用于串口功能 */
	unsigned long tmp = 0;
	 
	tmp = GPA0CON;
	tmp &= ~(0xff); //设置UART0对应的GPIO为UART功能
	tmp |= 0x22;
	GPA0CON = tmp;
	 
	/* 2.设置UART时钟源SCLK_UART */
	/* 2.1 CLK_SRC_DMC : bit[12]即MUX_MPLL_SEL=1, SCLKMPLLL使用MPLL的输出
	* 2.2 CLK_SRC_TOP1 : bit[12]即MUX_MPLL_USER_SEL_T=1, MUXMPLL使用SCLKMPLLL
	* 2.3 CLK_SRC_PERIL0 : bit[3:0]即UART0_SEL=6, MOUTUART0使用SCLKMPLL_USER_T
	* 所以, MOUTUART0即等于MPLL的输出, 800MHz
	*/
	/*
	* PWM_SEL = 0;
	* UART5_SEL = 0;
	* UART4_SEL = 6; // 串口时钟源选 SCLKMPLL_USER_T
	* UART3_SEL = 6;
	* UART2_SEL = 6;
	* UART1_SEL = 6;
	* UART0_SEL = 6;
	*/
	CLK_SRC_PERIL0 = ((0 << 24) | (0 << 20) | (6 << 16) | (6 << 12) | (6<< 8) | (6 << 4) | (6));
	 
	/*
	× 分频系数 = 7+1 = 8
	* 2.4 CLK_DIV_PERIL0 : bit[3:0]即UART0_RATIO=7,所以SCLK_UART0=MOUTUART0/(7+1)=100MHz
	*/
	CLK_DIV_PERIL0 = ((7 << 20) | (7 << 16) | (7 << 12) | (7 << 8) | (7 << 4) | (7));
	 
	/* 3.设置串口0相关 */
	/* 设置FIFO中断触发阈值
	* 使能FIFO
	*/
	UFCON0 = 0x111;
	 
	/* 设置数据格式: 8n1, 即8个数据位,没有较验位,1个停止位 */
	ULCON0 = 0x3;
	 
	/* 工作于中断/查询模式
	* 另一种是DMA模式,本章不使用
	*/
	UCON0 = 0x5;
	 
	/* SCLK_UART0=100MHz, 波特率设置为115200
	* 寄存器的值如下计算:
	* DIV_VAL = 100,000,000 / (115200 * 16) - 1 = 53.25
	* UBRDIVn0 = 整数部分 = 53
	* UFRACVAL0 = 小数部分 x 16 = 0.25 * 16 = 4
	*/
	UBRDIV0 = 53;
	UFRACVAL0 = 4;
}

//从串口获得一个字符
char getc(void)
{
	char c;
	/* 查询状态寄存器,直到有有效数据 */
	while (!(UTRSTAT0 & (1<<0)));

	c = URXH0; /* 读取接收寄存器的值 */

	return c;
}

//输出一个字符
void putc(char c)
{
	/* 查询状态寄存器,直到发送缓存为空 */
	while (!(UTRSTAT0 & (1<<2)));

	UTXH0 = c; /* 写入发送寄存器 */

	return;
}

//打印字符串
void puts(char *s)
{
	while (*s)
	{
		putc(*s);
		s++;
	}
}

//将数字按照十六进制格式打印
void puthex(unsigned long val)
{
	/* val = 0x1234ABCD */
	unsigned char c;
	int i = 0;

	putc('0');
	putc('x');

	for (i = 0; i < 8; i++)
	{
		c = (val >> ((7-i)*4)) & 0xf;
		if ((c >= 0) && (c <= 9))
		{
			c = '0' + c;
		}
		else if ((c >= 0xA) && (c <= 0xF))
		{
			c = 'A' + (c -  0xA);
		}
		putc(c);
	}
	putc('\n');
	putc('\r');
}

main.c文件主要是点亮LED等,内容如下:

//定义两个宏,方便操作使用到的寄存器
#define GPM4_CON (*(volatile int *)0x110002E0)
#define GPM4_DAT (*(volatile int *)0x110002E4)

#define GPM3CON	 (*(volatile int *)0x110002C0)
#define GPM3DAT	 (*(volatile int *)0x110002C4)

#define WTCON	 (*(volatile int *)0x10060000)

int main(unsigned int pc)
{
	unsigned int i;
	//设置GPM4_0引脚为输出
	GPM4_CON &= ~0xFFFF;			//GPM4CON寄存器的低4位清零
	GPM4_CON |= 0x1111;			//GPM4CON寄存器的bit0置1,设置为输出引脚

	//设置GPM4_0引脚为低电平
	GPM4_DAT &= ~0xF;			//GPM4DAT寄存器bit0清零,输出低电平

	//配置GPM3控制器的5、6、7为输入引脚
	GPM3CON &= ~0xFFF00000;

	puts("Lighting the LED!\n\r");

	while(1);

	return 0;
}

void printHello()
{
	puts("Hello tiny4412\n\r");
}

OK,涉及到的文件已经介绍完毕。下面是链接脚本和Makefile文件,只在这里粘贴出来,不做过多的介绍。方便大家试验使用。

undef_exception.lds文件的内容如下:

SECTIONS {
	//. = 0x02026400;		/* 链接地址 */
	. =   0x46000000;
	. = ALIGN(4);
	.text : 
	{ 
		*(.text) 
	}

	. = ALIGN(4);
	.rodata : 
	{
		*(.rodata)
	}

	. = ALIGN(4);
	.data : 
	{ 
		*(.data) 
	}

	. = ALIGN(4);
	bss_start = .;	/* bss段的开始位置 */
	.bss : 
	{ 
		*(.bss) *(.COMMON) 
	}
	bss_end = .;	/* bss段的结束位置 */
}

Makefile文件的内容如下:

undef_exception.bin : start.S clock_init_tiny4412.S mem_init_tiny4412.S main.c uart.c exception.c
	arm-linux-gcc -c -o start.o start.S
	arm-linux-gcc -c -nostdlib -fno-builtin -o exception.o exception.c
	arm-linux-gcc -c -nostdlib -fno-builtin -o uart.o uart.c
	arm-linux-gcc -c -o clock_init_tiny4412.o clock_init_tiny4412.S
	arm-linux-gcc -c -o mem_init_tiny4412.o mem_init_tiny4412.S	
	arm-linux-gcc -c -o main.o main.c
	arm-linux-ld -Tundef_exception.lds -g start.o clock_init_tiny4412.o mem_init_tiny4412.o uart.o main.o exception.o -o undef_exception.elf
	arm-linux-objcopy -O binary -S undef_exception.elf undef_exception.bin
	arm-linux-objdump -D undef_exception.elf > undef_exception.dis

clean:
	rm *.o *.elf *.dis *.bin

OK,现在所有文件已经介绍完毕了。当上述文件上传到服务器上面。执行make,并将undef_exception.bin文件烧写到SD卡中。查看串口输出和LED的情况。

输出如下:

Hello tiny4412                        /* 调试语句 */
Exception cpsr = 0x600001DB           /* 发生未定义指令异常后CPSR中的值 */
Undefined Instruction Exception!      /* 打印的字符串 */
Lighting the LED!                     /* 调试语句信息 */

从上面的打印的CPSR中的值0x600001DB,二进制是0b1100000000000000000000111011011;可以知道M0~M4是11011,查看上篇文章可以知道CPU确实进入了未定义指令异常模式。

关于软中断异常处理,我已经调试代码成功,但是有了上面的例子,就没有必要再写一篇文章来介绍了。如果读者感兴趣可以自己调试代码。

本文完结!

你可能感兴趣的:(tiny4412开发板学习记录)