AT91RM9200启动代码分析 | |
来源: ChinaUnix博客 日期: 2010.01.16 22:32 (共有0条评论) 我要评论 | |
AT91RM9200 的时钟源有4个: 慢时钟(SLK) 主时钟(Main Clock) PLLA,PLLB 在这里需要区别一个概念:主时钟和主机时钟 主时钟(main clock)是指输入主振荡器的时钟 主机时钟(mck)指CPU的时钟频率。 主机时钟可以在4个时钟源中选择(时钟选择器)一个作为本身的时钟 时钟设置流程 ![]() 启动代码中一些预定义的值 ;----------------------------------------------------------------------------- ;- ARM Core Mode and Status Bits ARM内核模式和状态位 ;------------------------------------------------------------------------------ ARM_MODE_USER EQU 0x10 ARM_MODE_FIQ EQU 0x11 ARM_MODE_IRQ EQU 0x12 ARM_MODE_SVC EQU 0x13 ARM_MODE_ABORT EQU 0x17 ARM_MODE_UNDEF EQU 0x1B ARM_MODE_SYS EQU 0x1F I_BIT EQU 0x80 F_BIT EQU 0x40 T_BIT EQU 0x20 ;------------------------------------------------------------------ ;- Stack Area Definition 堆栈段区域定义 ;------------------------------------------------------------------ IRQ_STACK_SIZE EQU 0x04 FIQ_STACK_SIZE EQU 0x04 ABT_STACK_SIZE EQU 0x04 UND_STACK_SIZE EQU 0x04 SVC_STACK_SIZE EQU 0x04 USER_STACK_SIZE EQU 0x100 ;---------------------------------------------------------------------------- 9200启动后时钟变化 由于系统复位后进入慢时钟状态,由慢时钟提供主机时钟源,但由于内置boot程序可能已经启动,需要在启动时重新设置慢时钟为主机时钟源。但主机时钟寄存器要求写入的值不能和当前值相同,所以要分成两步,代码如下: //第一步,将PMC_MCKR 修改一下,这里使其选择PLLB作为主机时钟源 ldr r1, = AT91C_BASE_PMC ;取得PMC的基地址 ldr r0, = AT91C_PMC_CSS_PLLB_CLK str r0, [r1, #PMC_MCKR] ;向PMC_MCKR寄存器写入数值 //通过测试状态寄存器 PMC_SR的第3位来判断时钟设置是否成功 mov r4, #0x8 MCKR_Loop ldr r3, [r1, #PMC_SR] and r3, r4, r3 cmp r3, #0x8 bne MCKR_Loop //第二步,修改PMC_MCKR使其选择慢时钟SCK作为主机时钟源 ldr r0, = AT91C_PMC_CSS_SLOW_CLK :OR:AT91C_PMC_PRES_CLK str r0, [r1, #PMC_MCKR] mov r4, #0x8 MCKR_Loop2 ldr r3, [r1, #PMC_SR] and r3, r4, r3 cmp r3, #0x8 bne MCKR_Loop2 //为了节省功耗关闭PLLs。 //实际上,在机器复位后PLLs 是被默认关闭的,但是在以前有一个boot程序已经运行过的情况//下,PLLs可能已经被打开,所以要在这个boot程序开始的时候要先关闭PLLs。 ldr r1, = AT91C_BASE_CKGR ; 取得CKGR 的基地址 ldr r0, = AT91C_CKGR_DIVA_0 str r0, [r1, #CKGR_PLLAR] : 关闭 PLLA ldr r0, = AT91C_CKGR_DIVB_0 str r0, [r1, #CKGR_PLLBR] : 关闭 PLLB 以上动作完成之后,系统的主机时钟源就是慢时钟。但是,系统正常运行时不可能用慢时钟作为时钟源,(比如此时用plla作为时钟源),就需要进一步的设置。 对于PLL时钟,它的时钟源是主振荡器时钟(即主时钟 main clock),所以要先将主时钟打开,代码如下: //打开主时钟 //MOSCEN 主时钟使能位;OSCOUNT 指定慢时钟周期数作为主振荡器启动时间 ldr r0, = AT91C_CKGR_MOSCEN:OR:AT91C_CKGR_OSCOUNT str r0, [r1, #CKGR_MOR] 选择主时钟作为主机时钟,设置时钟频率等工作等到后面用C语言来完成。这里将继续用汇编进行其余的初始化动作,直到运行至AT91F_LowLevelInit函数。 //为各种处理器模式设置堆栈 ; 加载堆栈基地址 add r0, pc,#-(8+.-StackData) ; r0 = pc – 8 - . + StackData ,让r0指向StackData ldmia r0, {r1-r6} ;从r0指向的地址单元加载数据到r1~r6中 ;- Set up Supervisor Mode and set SVC Mode Stack msr cpsr_c, #ARM_MODE_SVC : OR : I_BIT : OR : F_BIT bic r1, r1, #3 ; Insure word alignement mov sp, r1 ; Init stack SYS ;- Set up Interrupt Mode and set IRQ Mode Stack msr CPSR_c, #ARM_MODE_IRQ : OR : I_BIT : OR : F_BIT bic r2, r2, #3 ; 确保字对齐 mov sp, r2 ; 设置IRQ模式堆栈指针 ;- Set up Fast Interrupt Mode and set FIQ Mode Stack msr CPSR_c, #ARM_MODE_FIQ : OR : I_BIT : OR : F_BIT bic r3, r3, #3 ; Insure word alignement mov sp, r3 ; Init stack FIQ ;- Set up Abort Mode and set Abort Mode Stack msr CPSR_c, #ARM_MODE_ABORT : OR : I_BIT : OR : F_BIT bic r4, r4, #3 ; Insure word alignement mov sp, r4 ; Init stack Abort ;- Set up Undefined Instruction Mode and set Undef Mode Stack msr CPSR_c, #ARM_MODE_UNDEF : OR : I_BIT : OR : F_BIT bic r5, r5, #3 ; Insure word alignement mov sp, r5 ; Init stack Undef ;- Set up user Mode and set Undef Mode Stack msr CPSR_c, #ARM_MODE_SYS : OR : I_BIT : OR : F_BIT bic r6, r6, #3 ; Insure word alignement mov sp, r6 ; Init stack Undef b EndInitStack StackData DCD AT91_SVC_Stack_Begin DCD AT91_IRQ_Stack_Begin DCD AT91_FIQ_Stack_Begin DCD AT91_ABT_Stack_Begin DCD AT91_UND_Stack_Begin DCD AT91_USER_Stack_Begin EndInitStack ; 补偿主振荡器启动时间 ldr r0, =0x00000010 LoopOsc subs r0, r0, #1 bhi LoopOsc ; 调用C函数 AT91F_LowLevelInit() IMPORT AT91F_LowLevelInit ldr r0, = AT91F_LowLevelInit mov lr, pc bx r0 ;----------------------------------------------------- ; 读/改/写 CP15 控制寄存器 ;----------------------------------------------------- MRC p15, 0, r0, c1, c0,0 ; read cp15 control register (cp15 r1) in r0 ldr r3, =0xC0000080 ; Reset bit :Little Endian end fast bus mode ldr r4, =0xC0000000 ; Set bit :Asynchronous clock mode, Not Fast Bus BIC r0, r0, r3 ORR r0, r0, r4 MCR p15, 0, r0, c1, c0,0 ; write r0 in cp15 control registre (cp15 r1) ;----------------------------------------------------- ; 初始化C变量 ;----------------------------------------------------- add r2, pc,#-(8+.-CInitData) ; @ where to read values (relative) ldmia r2, {r0, r1, r3, r4} cmp r0, r1 ; Check that they are different beq EndRW LoopRW cmp r1, r3 ; Copy init data ldrcc r2, [r0], #4 ;从r0指向的地址加载数据到r2后,r0+4 strcc r2, [r1], #4 ;将r2中数据存储到r1指向的地址后,r1+4 bcc LoopRW EndRW mov r2, #0 LoopZI cmp r3, r4 ; Zero init strcc r2, [r3], #4 bcc LoopZI b EndInitC CInitData IMPORT |Image$$RO$$Limit| ; ROM 代码结束处 (=ROM 数据开始处) IMPORT |Image$$RW$$Base| ; Base of RAM to initialise IMPORT |Image$$ZI$$Base| ; Base and limit of area IMPORT |Image$$ZI$$Limit| ; Top of zero init segment DCD |Image$$RO$$Limit| ; End of ROM code (=start of ROM data) DCD |Image$$RW$$Base| ; Base of RAM to initialise DCD |Image$$ZI$$Base| ; Base and limit of area DCD |Image$$ZI$$Limit| ; Top of zero init segment EndInitC ;------------------------------------------------------------------------------ ;- Branch on C code Main function (with interworking) ;---------------------------------------------------- ;- Branch must be performed by an interworking call as either an ARM or Thumb ;- main C function must be supported. This makes the code not position- ;- independant. A Branch with link would generate errors ;------------------------------------------------------------------------------ IMPORT main _main __main EXPORT _main EXPORT __main ldr r0, =main mov lr, pc bx r0 ; 跳到mian() 函数中 ;------------------------------------------------------------------------------ ;- Loop for ever ;--------------- ;- End of application. Normally, never occur. ;- Could jump on Software Reset ( B 0x0 ). ;------------------------------------------------------------------------------ End b End END 由于之前打开了主时钟,但没有判断打开是否成功,所以AT91F_LowLevelInit首先通过AT91F_WaitForMainClockFrequency函数判断主时钟是否打开。代码略 主时钟打开之后,就可以进行真正的PLL时钟源设置,代码主体为AT91F_InitClocks函数。 由于时钟设置要求在一定的范围条件下,所以要首先判断是否满足时钟要求。主要分成4步: 1.先计算主时钟的频率,算法见datasheet。 2.通过MainClock和预设值(见函数的输入参数)来判断频率是否满足要求。 3.如果满足要求则把预设值写入到PLL控制寄存器。 4.最后一步当然是修改主机时钟源为需要的PLL,同样,需要分成两步。 本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/106983/showart_2151031.html |
文章评论(0条评论)
登录后参与讨论