1. 系统进入睡眠状态的过程
1.1 硬件相关电路
我们的产品使用GPF0/EINT0作为系统进入睡眠和从睡眠中唤醒的唤醒源,如下图所示:
EINT0作为唤醒源,而EINT0在S3C2443的datasheet中相关描述如下:
那么我们就需要知道触发这个中断(EINT0)的电平变化情况,是低电平触发?或是高电平触发?或是下降沿触发?或是上升沿触发?或是上升沿和下降沿触发?上面的设计是:在未按下按键(S5)时,EINT0是低电平,当按下按键时,EINT0是高电平,我们以松开按键时作为触发条件,也即下降沿时触发,初始化代码如下所示
1.2 系统进入睡眠的方式
WINCE6.0有三种方式让系统进入睡眠的状态,如下所示:
我们系统是采用第三种方式使系统进入睡眠状态的,
1.3 系统进入睡眠状态的调用流程
1.3.1 SetSystemPowerState()
在power按键驱动的IST中,当我们按下power键,在松开按键时,会调用SetSystemPowerState()函数,如下:
SetSystemPowerState( NULL, POWER_STATE_ON, POWER_FORCE );
下图是这个函数的定义
1.3.2 Power manager的相关调用
当OEM调用了SetSystemPowerState()函数,就转入power manager的工作流,执行下面的动作:
⑴ FileSystemPowerFunction():
⑵ PowerOffSystem()
⑶ Sleep()
1.3.3 内核的相关调用
Power manager的以上调用之后,接下来转入内核的调用流程,执行以下操作
其中power off GWES和文件系统进程是内核的工作,下面重点介绍OEMPowerOff()函数,这个函数在/Src/Common/Power/off.c下定义,这个函数体所实现的内容就对应到S3C2243 CPU的相关部分了,下图是系统进入睡眠状态之前的工作:
1.3.4 OEMPowerOff()函数
根据OEMPowerOff函数的流程图来说明这个函数体的功能。
下面分别介绍这个函数体主体部分:
⑴BSPPowerOff()函数
à pRTCPort->RTCCON=0x0;
这句主要目的就是在系统进入睡眠之前,禁止RTC控制使能,预防在系统进入睡眠状态后意外修改RTC寄存器的值,CPU的相关描述如下
à pADCPort->ADCCON|=(1<<2);
设置ADC的工作模式为standby mode,在睡眠状态下,触摸屏不需要工作,所以设置为standy mode,见下图:
à pIOPort->MISCCR|=(1<<12); //USB port = suspend
设置USB port为挂起模式,降低在睡眠时的功耗。
à pCLKPWR->USB_PHYPWR |= (0xf<<0);
见下图:
à pCLKPWR->PWRCFG &= ~(1<<4);
关闭对usb物理端口的供电。
à pCLKPWR->USB_CLKCON &= ~(1<<31);
见下图
à pCLKPWR->INFORM0 = 0x2BED;
这个寄存器可用于在进入睡眠前保留一些重要或者特别目的的数据,以便唤醒过程或唤醒后可以使用。
à 关闭背光灯和LCD
⑵ save system registers
…………………………………..
saveArea[5] = INPORT32(&pIOPort->GPCCON);
saveArea[6] = INPORT32(&pIOPort->GPCDAT);
saveArea[7] = INPORT32(&pIOPort->GPCUDP);
…………………………………
把GPIO寄存器的值保存在saveArea[]数组中,以便唤醒后恢复寄存器保存的数值。
⑶ ConfigStopGPIO函数
在这个函数可以设置好作为唤醒睡眠的GPIO口,比如GPF0/EINT0作为唤醒的GPIO口,就要在这里设置好,也可在OALCPUPowerOff函数中设置好,其他GPIO口一般设置为输入,已减少系统在睡眠时的功耗。
⑷ OALCPUPowerOff函数
这个函数在/Src/Oal/Oallib/startup.s中定义,如下所示
LEAF_ENTRY OALCPUPowerOff
; 1. Push SVC state onto our stack
stmdb sp!, {r4-r12}
stmdb sp!, {lr}
/***************************************************************/
我们知道R13常用作堆栈指针(sp),用于保存当前堆栈地址。SVC:表示处理器模式为管理模式。Stm指令是多寄存器存储指令,在此表示把r4到r12这9个寄存器中的值存储到基址寄存器(sp)所指示的一片连续存储器中,stm后面的db表示每次传送前地址值减;!表示数据加载与存储完毕之后,将最后的地址写入基址寄存器,如不使用!,则基址寄存器的内容不改变。假设sp=0x90020,stmdb sp!, {r4-r12}的操作如下:
第一步:在存储前,0x90050的值减4(对于ARM指令是4,对Thumb指令是2),也即为0x9004c。
第二步:把寄存器r12的值存储到0x9004c指向的存储区域。
第三步:0x9004c-4=0x90048。
第四步:把寄存器r11的值存储到0x90048指向的存储区域。
……………………………………
最后一步:就是sp=0x90030
lr:R14寄存器也称为子程序连接器(Subroutine Link Register)或连接寄存器LR。
/***************************************************************/
; 2. Save MMU & CPU Register to RAM
ldr r3, =SLEEPDATA_BASE_VIRTUAL ; base of Sleep mode storage
ldr r2, =Awake_address ; store Virtual return address
str r2, [r3], #4
mrc p15, 0, r2, c1, c0, 0 ; load r2 with MMU Control
ldr r0, =MMU_CTL_MASK ; mask off the undefined bits
bic r2, r2, r0
str r2, [r3], #4 ; store MMU Control data
mrc p15, 0, r2, c2, c0, 0 ; load r2 with TTB address.
ldr r0, =MMU_TTB_MASK ; mask off the undefined bits
bic r2, r2, r0
str r2, [r3], #4 ; store TTB address
mrc p15, 0, r2, c3, c0, 0 ; load r2 with domain access control.
str r2, [r3], #4 ; store domain access control
str sp, [r3], #4 ; store SVC stack pointer
mrs r2, spsr
str r2, [r3], #4 ; store SVC status register
mov r1, #Mode_FIQ:OR:I_Bit:OR:F_Bit ; Enter FIQ mode, no interrupts
msr cpsr, r1
mrs r2, spsr
stmia r3!, {r2, r8-r12, sp, lr} ; store the FIQ mode registers
mov r1, #Mode_ABT:OR:I_Bit:OR:F_Bit ; Enter ABT mode, no interrupts
msr cpsr, r1
mrs r0, spsr
stmia r3!, {r0, sp, lr} ; store the ABT mode Registers
mov r1, #Mode_IRQ:OR:I_Bit:OR:F_Bit ; Enter IRQ mode, no interrupts
msr cpsr, r1
mrs r0, spsr
stmia r3!, {r0, sp, lr} ; store the IRQ Mode Registers
mov r1, #Mode_UND:OR:I_Bit:OR:F_Bit ; Enter UND mode, no interrupts
msr cpsr, r1
mrs r0, spsr
stmia r3!, {r0, sp, lr} ; store the UND mode Registers
mov r1, #Mode_SYS:OR:I_Bit:OR:F_Bit ; Enter SYS mode, no interrupts
msr cpsr, r1
stmia r3!, {sp, lr} ; store the SYS mode Registers
mov r1, #Mode_SVC:OR:I_Bit:OR:F_Bit ; Back to SVC mode, no interrupts
msr cpsr, r1
; 3. do Checksum on the Sleepdata
ldr r3, =SLEEPDATA_BASE_VIRTUAL ; get pointer to SLEEPDATA
mov r2, #0
ldr r0, =SLEEPDATA_SIZE ; get size of data structure (in words)
30
ldr r1, [r3], #4
and r1, r1, #0x1
mov r1, r1, LSL #31
orr r1, r1, r1, LSR #1
add r2, r2, r1
subs r0, r0, #1
bne %b30
/****************************************************************/
计算睡眠数据的checksum,保存在r2寄存器中
/****************************************************************/
ldr r0, =vINFORM3
str r2, [r0] ; Store in Power Manager Scratch pad register
/****************************************************************/
把保存在r2寄存器中的睡眠数据的checksum保存到INFORM3寄存器中,以便唤醒过程计算checksum时比较。
/****************************************************************/
; 4. Interrupt Disable
ldr r0, =vINTBASE
mvn r2, #0
str r2, [r0, #oINTMSK]
str r2, [r0, #oSRCPND]
str r2, [r0, #oINTPND]
;; 5. Cache Flush
[ {TRUE}
bl OALClearUTLB
bl OALFlushICache
ldr r0, = (DCACHE_LINES_PER_SET - 1)
ldr r1, = (DCACHE_NUM_SETS - 1)
ldr r2, = DCACHE_SET_INDEX_BIT
ldr r3, = DCACHE_LINE_SIZE
bl OALFlushDCache
]
; 6. Setting Wakeup External Interrupt(EINT0) Mode
ldr r0, =vGPIOBASE
ldr r1, =0x5502
str r1, [r0, #oGPFCON]
/****************************************************************/
设置GPF0为EINT0,并且设置为下降沿触发,只要这里的设置准确才能正常从睡眠状态唤醒系统。
/****************************************************************/
ldr r4, =vRSTCON
ldr r5, =0x0ff80
str r5, [r4]
/****************************************************************/
/****************************************************************/
ldr r4, =vOSCSET
ldr r5, =0x8000
str r5, [r4]
/****************************************************************/
/****************************************************************/
ldr r4, =vPWRCFG
ldr r5, =0x8201
str r5, [r4]
/****************************************************************/
/****************************************************************/
ldr r4, =vPWRMODE
ldr r5, [r4]
bic r5, r5, #0xff00
bic r5, r5, #0x00ff
ldr r6, =0x2BED
orr r5, r5, r6 ; Power Off Mode
str r5, [r4] ; Power Off !!
b .
/****************************************************************/
/****************************************************************/
1.4 系统进入休眠前必须的动作
在系统进入休眠前,正确设置外部唤醒中断,才能够唤醒CPU.一般来说,正确设置唤醒中断源,有三个要点。
⑴ 把对应的GPIO设置为中断功能
⑵明确外部中断触发条件,比如我们把这个唤醒用的中断源所对应的IO接到一个按键上,希望通过按下按键来实现唤醒。那么就得明确,当按下这个按键时,IO口上的电平会发生什么样的变化。
⑶ 设置EXTINTn寄存器,按照按键按下时IO电平的变化条件来设置。比如当按下按键时,IO口上的电平会发生从高到低的变化,那么我们就设置对应的EXTINTn,使得中断触发条件为Falling edge trigeerde,即下降沿触发。
2. 系统从睡眠状态唤醒的过程
2.1 唤醒
当按下power键(GPF0/EINT0端口产生下降沿),程序就从nboot开始执行,也即从/Src/Bootloader/Stepldr/statup.s文件的入口ResetHandler开始
Stepldr的入口函数startup.s是由stepldr目录下的sources文件下“EXEENTRY=StartUp”来指定的,startup.s函数的主要功能如下所示:
1)startup.s函数的入口处
STARTUPTEXT
LEAF_ENTRY StartUp ;用于指定Startup.s函数的入口点
b ResetHandler ;无条件跳转到ResetHandler函数
b .
……………………….
b .
;-----------------------------------
; Steppingstone loader entry point.
;-----------------------------------
ResetHandler
ldr r0, =WTCON ; disable the watchdog timer.
mov r1,#0
str r1, [r0]
……………………
2) 禁止看门狗,屏蔽所有的中断及设置中断模式
ResetHandler
ldr r0, =WTCON ; disable the watchdog timer.
mov r1,#0
str r1, [r0]
ldr r0, = GPACDH
ldr r1, = 0x1AA8A
str r1, [r0]
/**********************************************************************/
GPACON和GPADAT在S3C2443中已不存在,被GPACDL和GPACDH代替,其作用如下:
GPACDL:Configuration and data register for port A low
GPACDH:Configuration and data register for port A high
配置为功能端,但不甚清楚如此配置的原因。
/**********************************************************************/
ldr r0, = GPFCON
ldr r1, = 0x5500
2
str r1, [r0]
ldr r0, =INTMSK ; mask all first-level interrupts.
ldr r1, =0xffffffff
str r1, [r0]
ldr r0, =INTSUBMSK ; mask all second-level interrupts.
ldr r1, =0x1fffffff
str r1, [r0]
/**********************************************************************/
屏蔽子中断和中断,从而在stepldr阶段不能使用中断服务功能。
/**********************************************************************/
ldr r0, = INTMOD
mov r1, #0x0 ; set all interrupt as IRQ
str r1, [r0]
/**********************************************************************/
设置所有的中断为IRQ中断。
/**********************************************************************/
3)设置时钟频率
ldr r0,=CLKDIV0 ; Set Clock Divider
ldr r1,[r0]
bic r1,r1,#0x37 ; clear HCLKDIV, PREDIV, PCLKDIV
bic r1,r1,#(0xf<<9) ; clear ARMCLKDIV
ldr r2,=((Startup_ARMCLKdiv<<9)+(Startup_PREdiv<<4)+(Startup_PCLKdiv<<2)+(Startup_HCLKdiv))
orr r1,r1,r2
str r1,[r0]
/**********************************************************************/
先清除PREDIV、PCLKDIV、HCLKDIV和ARMCLKDIV,在重新根据系统的需要来设置这几个参数,Startup_ARMCLKdiv等这几个参数在stepldr目录下的option.inc下定义。
ldr r0,=LOCKCON0 ; Set lock time of MPLL. added by junon
mov r1,#0xe10 ; Fin = 12MHz - 0x800, 16.9844MHz - 0xA00
str r1,[r0]
LOCKCON0是MPLL锁存时间的统计寄存器,启用PLL之后,由这个寄存器的值来决定为ARMCLK,HCLK和PCLK服务的MPLL锁存时间的计数值,典型情况下,这个值必须大于300us。
/**********************************************************************/
ldr r0,=LOCKCON1 ; Set lock time of EPLL. added by junon
mov r1,#0x800 ; Fin = 12MHz - 0x800, 16.9844MHz - 0xA00
str r1,[r0]
/**********************************************************************/
LOCKCON1是EPLL锁存时间的统计寄存器,启用PLL之后,由这个寄存器的值来决定为ARMCLK,HCLK和PCLK服务的MPLL锁存时间的计数值,典型情况下,这个值必须大于150。
/**********************************************************************/
ldr r0,=MPLLCON ; Set MPLL
ldr r1,=((0<<24)+(Startup_Mdiv<<16)+(Startup_Pdiv<<8)+(Startup_Sdiv))
str r1,[r0]
ldr r0,=EPLLCON ; Set EPLL
ldr r1,=((0<<24)+(Startup_EMdiv<<16)+(Startup_EPdiv<<8)+(Startup_ESdiv))
str r1,[r0]
PLL的输出频率由MPLLCON和EPLLCON寄存器的值来决定,其值尽量根据S3C2443用户手册中推荐使用的表来选择
MPLLCON相关参数的表:
EPLLCON相关参数的表:
ldr r0,=CLKSRC ; Select MPLL clock out for SYSCLK
ldr r1,[r0]
orr r1,r1,#0x50
str r1,[r0]
设置CLKSRC中的ESYSCLK选择方式为EPLL输出和MSYSCLK选择方式为MPLL输出,见下图:
4) 设置总线的同步方式
bl MMU_SetAsyncBusMode
这个函数的代码如下所示:
LEAF_ENTRY MMU_SetAsyncBusMode
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #R1_nF:OR:R1_iA
mcr p15, 0, r0, c1, c0, 0
mov pc, lr
5) 初始化内存控制器
bl InitMEM
这个函数体如下所示:
InitMEM
ldr r0,=GPKCON
ldr r1,=0xaaaaaaaa ; set Sdata[31:16]
str r1, [r0]
/**********************************************************************/
GPKCON就是SDATACFG寄存器,这是32位SDRAM的配置寄存器,为了控制32位的SDRAM,在内存初始化之前,SDATACFG应该设置为0xAAAAAAAA
/**********************************************************************/
add r0, pc, #MEMDATA - (. + 8)
ldr r1,=BANKCFG ;
add r2, r0, #16 ;End address of MEMDATA
110
/**********************************************************************/
先将存储器地址为r0的字数据读入到寄存器r3中,然后将新地址r0+4写入r0.
ldr r3, [r0], #4
将寄存器r3的值写入存储器地址为r0的寄存器,也即BANKCFG,也就是把MEMDATA标号处的第一个分配的单元数据给BANKCFG(0x48000000)赋值,然后将新地址r1+4写入r1,这时的r1指向BANKCON1(0x48000008)
str r3, [r1], #4
比较r2和r0的值,如果相等,表示对BANKCFG、BANKCON1、BANKCON2和BANKCON3这四个寄存器的初始化已经完成,如果没有,则跳到上面110处继续执行,直到对这四个寄存器始
化完成。
cmp r2, r0
bne %B110
先清除BANKCON1的最低两位,再赋值01给这两位,用于产生一个PALL命令
ldr r2,=BANKCON1
ldr r1,[r2]
bic r1,r1,#(0x3<<0)
orr r1,r1,#(0x1<<0) ; 4nd : Issue a PALL command
str r1,[r2]
对REFRESH的DRAM刷新周期赋值,这个值REFCYC=refresh period(这个和SDRAM的刷新周期有关,本系统使用K4S56163PF,其刷新周期为64ms,计算时是以s为单位的)*HCLK
ldr r4,=REFRESH ; 5fh : refresh cycle every 255-clock cycles
ldr r0,=0xff
str r0,[r4]
mov r0, #0x100 ; 6th : wait 2 auto - clk
120 subs r0, r0,#1;
bne %B120
bic r1,r1,#(0x3<<0) ; 7th : Issue a MRS command
orr r1,r1,#(0x2<<0)
str r1,[r2]
ldr r4,=REFRESH ; 8fh : refresh normal
ldr r0,=REFCYC
str r0,[r4]
orr r1,r1,#(0x3<<0) ; 9th : Issue a EMRS command
str r1,[r2]
bic r1,r1,#(0x3<<0) ; 10th : Issue a Normal mode
str r1,[r2]
// R14也称作子程序连接寄存器lr,当执行BL子程序调用指令时,lr得到R15(程序计数器PC)的备份,下面这行语句可以实现返回到跳转的地址处。
mov pc, lr
6)静态内存控制器(staic memory controller,SMC)的初始化,这个控制器在本开发板中用于控制nor flash,而mx300没有使用nor flash,所以应该不需要对SMC进行初始化.
bl InitSSMC
InitSSMC函数的定义如下:
InitSSMC
;Set SSMC Memory parameter control registers : AMD Flash
ldr r0,=SMBIDCYR0
ldr r1,=IDCY0
str r1,[r0]
ldr r0,=SMBWSTRDR0
ldr r1,=WSTRD0
str r1,[r0]
ldr r0,=SMBWSTWRR0
ldr r1,=WSTWR0
str r1,[r0]
ldr r0,=SMBWSTOENR0
ldr r1,=WSTOEN0
str r1,[r0]
ldr r0,=SMBWSTWENR0
ldr r1,=WSTWEN0
str r1,[r0]
ldr r0,=SMBCR0
ldr r1,=(SMBCR0_2+SMBCR0_1+SMBCR0_0)
str r1,[r0]
ldr r0,=SMBWSTBRDR0
ldr r1,=WSTBRD0
str r1,[r0]
ldr r0,=SMBWSTBRDR0
ldr r1,=WSTBRD0
str r1,[r0]
ldr r0,=SSMCCR
ldr r1,=((MemClkRatio<<1)+(SMClockEn<<0))
str r1,[r0]
;ldr r0,=SMBWSTRDR5
;ldr r1,=0xe
;str r1,[r0]
mov pc, lr
7) S3C2443X的系统控制器(system controller)有三大功能:复位管理,产生时钟和电源管理,下面先介绍复位管理。
当给S3C2443X的系统上电(power-on)时,外部设备必须插入复位来初始化S3C2443X的内部状态,S3C2443X有四种复位类型,在复位时,系统控制器的复位控制器能够使系统进入四种复位类型预先定义的状态中,这四种复位类型是:硬件复位,看门狗复位,软件复位和唤醒复位
I:硬件复位,当nRESET引脚插入低电平时产生硬件复位,这种类型的复位是一种uncompromised,unmaskable和complete reset,以硬件复位方式启动时,除了RTC以外的系统所有单元都被初始化为可知的状态(初始化状态)。
II:看门狗复位,看门狗计数器监测到设备异常的状态时就会产生看门狗复位。
III:软件复位(software reset),当对SWRST寄存器的每一位写1时,就产生软件复位。
IV:唤醒复位(wakeup reset),当系统从sleep mode中唤醒时,就产生唤醒复位。
通过reset状态寄存器来得到复位状态。
ldr r1, =RSTSTAT
ldr r0, [r1]
如果是从sleep中唤醒,则RSTSTAT[3]=1,否认RSTSTAT[3]=0。如果是从sleep中唤醒,则指令TST运算结果为非零,影响CPSR的Z标志位,这时Z=0;如果不是从sleep中唤醒,则指令TST运算结果为零,此时Z=1。
tst r0, #0x8
如果从sleep中唤醒,则接着执行下面的指令;如果不是(正常的启动),则跳到下面标号为2处接着执行。beq指令在Z标志位置位时(Z=1,表示上面的TST指令运算结果为0,从而知//道RSTSTAT[3]=0,而这表明系统不是从sleep中唤醒。)跳转。
beq %F2 ; if not wakeup from PowerOffmode Skip
如果是从sleep mode中唤醒,接下来深入及具体判断是通过RTC tick,或是RTC alarm,还是EINT终端模式来从sleep mode中唤醒。
ldr r1, =WKUPSTAT
ldr r0, [r1]
判断是否通过RTC alarm来从sleep mode中唤醒系统
tst r0, #(1<<1)
如果是通过RTC alarm来唤醒,则WKUPSTAT[1]=1,那么上面tst运算结果为非零,则CPSR的Z=0,那么就接着往下执行;如果不是通过alarm来唤醒,则WKUPSTAT[1]=0,那么上面tst运算结果为零,则CPSR的Z=1,那么跳转到下面标号6处执行
beq %f6 ; if not wakeup from PowerOffmode Skip
如果是通过RTC alarm来唤醒,则接着到此处执行,设置RTCCON[0]=1:启动RTC控制。
ldr r1,=RTCCON
ldr r3,[r1]
orr r3,r3,#0x1
str r3,[r1]
enable RTC alarm控制的相关功能项
ldr r1,=RTCALM
ldr r3,=0x7f
str r3,[r1]
设置RTC alarm秒,分,小时,日,月和年控制寄存器,以便下次唤醒所用。
ldr r1, =BCDSEC
ldr r2, =ALMSEC
ldr r3,[r1]
str r3,[r2]
ldr r1, =BCDMIN
ldr r2, =ALMMIN
ldr r3,[r1]
str r3,[r2]
ldr r1, =BCDHOUR
ldr r2, =ALMHOUR
ldr r3,[r1]
str r3,[r2]
ldr r1, =BCDDATE
ldr r2, =ALMDATE
ldr r3,[r1]
str r3,[r2]
ldr r1, =BCDMON
ldr r2, =ALMMON
ldr r3,[r1]
str r3,[r2]
ldr r1, =BCDYEAR
ldr r2, =ALMYEAR
ldr r3,[r1]
str r3,[r2]
ldr r1,=RTCCON
ldr r3,[r1]
bic r3,r3,#0x1
str r3,[r1]
唤醒的复位方式启动,但又不是通过RTC alarm唤醒,则直接跳到此处执行;如果是通过RTC alarm唤醒,则先重新启动RTC alarm控制及设置相应的控制寄存器再接着到标号6处执行。
6
ldr r2, =0x200000 ; offset into the RAM
add r2, r2, #0x30000000 ; add physical base
mov pc, r2 ; & jump to StartUp address
nop
nop
nop
b .
跳转到 RAM中的一个地址去执行,这个地址是0x32000000。那么熟悉2443 WINCE启动的朋友们应该明白了,这个地址就是Bootloader把NandFlash里的数据装载完毕后,跳转执行的地址。那么在这里,跳转到0x302000000这个地址后,WINCE系统就会被装载了,也就是说WINCE的操作系统被唤醒了。
2
如果不是从sleep中唤醒,而是其他的复位方式,将接着从这里执行。
ldr r1, =RSTSTAT
ldr r0, [r1]
tst指令用于判断当前的复位方式是否时软件复位:如果是软件复位,则RSTSTAT[5]=1,tst指令的运行结果为非零,则CPSR的Z=0;如果不是,则RSTSTAT[5]=0,tst指令的运算结果为零,则CPSR的Z=1
tst r0, #(1<<5)
如果是软件复位,根据上面tst指令的运算,知道CPSR的Z=0,则接着执行下面的语句;如果不是软件复位,则跳转到BringUpWinCE处接着执行
beq BringUpWinCE ; if not wakeup from PowerOffmode Skip
如果是软件复位,从此处接着执行
JumpToRAM
0x30200000(=0x30000000+0x200000)是NK.bin的image start地址
ldr r2, =0x200000 ; offset into the RAM
add r2, r2, #0x30000000 ; add physical base
直接跳转到SDRAM的0x30200000从WINCE操作系统内核映像的入口处执行,软件复位方式只用到了stepldr,然后就直接跳转到内核中执行了。
mov pc, r2 ; & jump to StartUp address
nop
nop
nop
b .//???????????????????????????????????????????????????????????????????????
如果不是软件复位,这时应该是硬件复位(或是看门狗复位,应该也是最常用的上电启动),紧接着此处执行
BringUpWinCE
[ {TRUE} ; DonGo
; Clear RAM.
;
mov r1,#0
mov r2,#0
mov r3,#0
mov r4,#0
mov r5,#0
mov r6,#0
mov r7,#0
mov r8,#0
ldr r0,=0x30000000 ; Start address (physical 0x3000.0000).
ldr r9,=0x04000000 ; 64MB of RAM.
20
stm指令用于将寄存器列表(在此r1到r8)所指示的多个寄存器中的值存入到由基址寄存器所指向的一片连续存储器中,条件码ia表示每次传送后地址值(基址寄存器的值,在此的初始值为0x30000000)加,下面stmia指令的实际意义:用r1的值(0)给0x30000000指向的存储器赋值,在此实际是对SDRAM清零,因为0x30000000到0x34000000这64MB的空间是用于SDRAM的,用r2的值给0x30000004指向的存储器赋值,如此类推,用r8的值给0x30000028指向的存储器赋值,这次传送完成后,使r0=0x30000032。
stmia r0!, {r1-r8}
上面stmia指令每进行一次数据传送,0x04000000相应减去32(4*8),用于判断对64MB的
SDRAM清零动作是否完成
subs r9, r9, #32
如果清零完成,则接着执行下面的语句;否则会跳到到上面标号为20处继续对SDRAM的清零。
bne %B20
]
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/chinesedragon2010/archive/2010/07/21/5753720.aspx