AT91RM9200启动代码分析

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后,r2+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,同样,需要分成两步。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值