在前面的两篇文章中已经介绍了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确实进入了未定义指令异常模式。
关于软中断异常处理,我已经调试代码成功,但是有了上面的例子,就没有必要再写一篇文章来介绍了。如果读者感兴趣可以自己调试代码。
本文完结!