S3C2440启动代码2440init.s彻底解析
扫描二维码
随时随地手机看文章
2440可以选择nand启动和nor启动,这两者之间的关系通过一个按键来选择
这个OM0有何玄机,在数据手册中有这么一段
可以看到,只要将OM1接地,那么通过OM0选择1或选择0就可以选择NAND启动或者16位宽RAM启动了(当然,还得设置一些东西,下面就说),
Nanaflash启动经历的过程相当于首先,2440自动从nand里面读取4K的代码,这4K代码将nand里面的数据拷贝到ram中,然后跳转到ram中执行代码,为什么是4K,因为
2440.s的启动代码需要包含几个文件
2440addr.inc
包含2440内部寄存器地址
Memcfg.inc
包含2440各个bank的内存配置数据
Option.inc
包含2440的各种时钟配置代码
Nand.c
包含nanaflash的读写函数
好了,分析开始
;REFRESH寄存器[22]bit : 0- auto refresh; 1 - self refresh
BIT_SELFREFRESH EQU (1<<22) ;用于节电模式中,SDRAM自动刷新
;处理器模式常量: CPSR寄存器的后5位决定目前处理器模式M[4:0]
USERMODE EQU 0x10
FIQMODE EQU 0x11
IRQMODE EQU 0x12
SVCMODE EQU 0x13
ABORTMODE EQU 0x17
UNDEFMODE EQU 0x1b
MODEMASK EQU 0x1f ;M[4:0]//模式计算掩码
NOINT EQU 0xc0//除去模式之后的剩余值
CPSR的说明
;定义处理器各模式下堆栈地址常量
UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~ _STACK_BASEADDRESS定义在option.inc中
SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~
UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~
AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~
IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~
FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~
剩下的请查看以下代码
;汇编不能使用include包含头文件,所有用Get;汇编也不认识*.h文件,所有只能用*.incGEToption.inc;定义芯片相关的配置GETmemcfg.inc;定义存储器配置GET2440addr.inc;定义了寄存器符号;REFRESH寄存器[22]bit:0-autorefresh;1-selfrefreshBIT_SELFREFRESHEQU(1<<22);用于节电模式中,SDRAM自动刷新;处理器模式常量:CPSR寄存器的后5位决定目前处理器模式M[4:0]USERMODEEQU0x10FIQMODEEQU0x11IRQMODEEQU0x12SVCMODEEQU0x13ABORTMODEEQU0x17UNDEFMODEEQU0x1bMODEMASKEQU0x1f;M[4:0]NOINTEQU0xc0;定义处理器各模式下堆栈地址常量UserStackEQU(_STACK_BASEADDRESS-0x3800);0x33ff4800~_STACK_BASEADDRESS定义在option.inc中SVCStackEQU(_STACK_BASEADDRESS-0x2800);0x33ff5800~UndefStackEQU(_STACK_BASEADDRESS-0x2400);0x33ff5c00~AbortStackEQU(_STACK_BASEADDRESS-0x2000);0x33ff6000~IRQStackEQU(_STACK_BASEADDRESS-0x1000);0x33ff7000~FIQStackEQU(_STACK_BASEADDRESS-0x0);0x33ff8000~;arm处理器有两种工作状态1.arm:32位这种工作状态下执行字对准的arm指令2.Thumb:16位这种工作状;态执行半字对准的Thumb指令;因为处理器分为16位32位两种工作状态程序的编译器也是分16位和32两种编译方式所以下面的程序用;于根据处理器工作状态确定编译器编译方式;code16伪指令指示汇编编译器后面的指令为16位的thumb指令;code32伪指令指示汇编编译器后面的指令为32位的arm指令;;Arm上电时处于ARM状态,故无论指令为ARM集或Thumb集,都先强制成ARM集,待init.s初始化完成后;再根据用户的编译配置转换成相应的指令模式。为此,定义变量THUMBCODE作为指示,跳转到main之前;根据其值切换指令模式;;这段是为了统一目前的处理器工作状态和软件编译方式(16位编译环境使用tasm.exe编译;Checkiftasm.exe(armasm-16...@ADS1.0)isused.GBLLTHUMBCODE;定义THUMBCODE全局变量注意EQU所定义的宏与变量的区别[{CONFIG}=16;如果发现是在用16位代码的话(编译选项中指定使用thumb指令)THUMBCODESETL{TRUE};一方面把THUMBCODE设置为TURECODE32;另一方面暂且把处理器设置成为ARM模式,以方便初始化";(|表示else)如果编译选项本来就指定为ARM模式THUMBCODESETL{FALSE};把THUMBCODE设置为FALSE就行了];结束MACRO;一个根据THUMBCODE把PC寄存的值保存到LR的宏MOV_PC_LR;宏名称[THUMBCODE;如果定义了THUMBCODE,则bxlr;在ARM模式中要使用BX指令转跳到THUMB指令,并转换模式.;bx指令会根据PC最后1位来确定是否进入thumb状态|;否则,movpc,lr;如果目标地址也是ARM指令的话就采用这种方式]MEND;宏定义结束标志MACRO;和上面的宏一样,只是多了一个相等的条件MOVEQ_PC_LR[THUMBCODEbxeqlr|moveqpc,lr]MEND;=======================================================================================;下面这个宏是用于第一次查表过程的实现中断向量的重定向,如果你比较细心的话就是发现;在_ISR_STARTADDRESS=0x33FF_FF00里定义的第一级中断向量表是采用型如Handle***的方式的.;而在程序的ENTRY处(程序开始处)采用的是bHandler***的方式.;在这里Handler***就是通过HANDLER这个宏和Handle***建立联系的.;这种方式的优点就是正真定义的向量数据在内存空间里,而不是在ENTRY处的ROM(FLASH)空间里,;这样,我们就可以在程序里灵活的改动向量的数据了.;========================================================================================;;这段程序用于把中断服务程序的首地址装载到pc中,有人称之为“加载程序”。;本初始化程序定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首地址。;每个字空间都有一个标号,以Handle***命名。;在向量中断模式下使用“加载程序”来执行中断服务程序。;这里就必须讲一下向量中断模式和非向量中断模式的概念;向量中断模式是当cpu读取位于0x18处的IRQ中断指令的时候,系统自动读取对应于该中断源确定;地址上的指令取代0x18处的指令,通过跳转指令系统就直接跳转到对应地址;函数中节省了中断处理时间提高了中断处理速度标例如ADC中断的向量地址为0xC0,则在0xC0处;代放如下码:ldrPC,=HandlerADC当ADC中断产生的时候系统会自动跳转到HandlerADC函数中;非向量中断模式处理方式是一种传统的中断处理方法,当系统产生中断的时候,系统将interrupt;pending寄存器中对应标志位置位然后跳转到位于0x18处的统一中断函数中;该函数通过读取interruptpending寄存器中对应标志位来判断中断源并根据优先级关系再跳到;对应中断源的处理代码中;;H|------|H|------|H|------|H|------|H|------|;|///||///||///||///||///|;|------|<----sp|------||------||------||------|<------sp;L|||------|<----spL|------||-isr--||------|isr==>pc;|||||--r0--|<----sp|---r0-|<----spL|------|r0==>r0;(0)(1)(2)(3)(4)MACRO$HandlerLabelHANDLER$HandleLabel$HandlerLabel;标号subsp,sp,#4;(1)减少sp(用于存放转跳地址)stmfdsp!,{r0};(2)把工作寄存器压入栈(lrdoesnotpushbecauseitreturntooriginaladdress)ldrr0,=$HandleLabel;将HandleXXX的址址放入r0ldrr0,[r0];把HandleXXX所指向的内容(也就是中断程序的入口)放入r0strr0,[sp,#4];(3)把中断服务程序(ISR)压入栈ldmfdsp!,{r0,pc};(4)用出栈的方式恢复r0的原值和为pc设定新值(也就完成了到ISR的转跳)MEND;=========================================================================================;在这里用IMPORT伪指令(和c语言的extren一样)引入|Image$$RO$$Base|,|Image$$RO$$Limit|...;这些变量是通过ADS的工程设置里面设定的ROBase和RWBase设定的,;最终由编译脚本和连接程序导入程序.;那为什么要引入这玩意呢,最简单的用处是可以根据它们拷贝自已;==========================================================================================;Image$$RO$$Base等比较古怪的变量是编译器生成的。RO,RW,ZI这三个段都保存在Flash中,但RW,ZI在Flash中;的地址肯定不是程序运行时变量所存储的位置,因此我们的程序在初始化时应该把Flash中的RW,ZI拷贝到RAM的;对应位置。一般情况下,我们可以利用编译器替我们实现这个操作。比如我们跳转到main()时,使用b__Main,;编译器就会在__Main和Main之间插入一段汇编代码,来替我们完成RW,ZI段的初始化。如果我们使用bMain,;那么初始化工作要我们自己做。编译器会生成如下变量告诉我们RO,RW,ZI三个段应该位于什么位置,但是它并;没有告诉我们RW,ZI在Flash中存储在什么位置,实际上RW,ZI在Flash中的位置就紧接着RO存储。我们知道了;Image$$RO$$Base,Image$$RO$$Limit,那么Image$$RO$$Limit就是RW(ROMdata)的开始。IMPORT|Image$$RO$$Base|;BaseofROMcodeIMPORT|Image$$RO$$Limit|;EndofROMcode(=startofROMdata)IMPORT|Image$$RW$$Base|;BaseofRAMtoinitialiseIMPORT|Image$$ZI$$Base|;BaseandlimitofareaIMPORT|Image$$ZI$$Limit|;tozeroinitialise;这里引入一些在其它文件中实现在函数,包括为我们所熟知的main函数;IMPORTMMU_SetAsyncBusMode;IMPORTMMU_SetFastBusMode;hzhIMPORTMain;从这里开始就是正真的代码入口了!AREAInit,CODE,READONLY;这表明下面的是一个名为Init的代码段ENTRY;定义程序的入口(调试用)EXPORT__ENTRY;导出符号_ENTRY,但在那用到就还没查明__ENTRYResetEntry;1)Thecode,whichconvertstoBig-endian,shouldbeinlittleendiancode.;2)ThefollowinglittleendiancodewillbecompiledinBig-Endianmode.;Thecodebyteordershouldbechangedasthememorybuswidth.;3)Thepseudoinstruction,DCDcannotbeusedherebecausethelinkergenerateserror.;条件编译,在编译成机器码前就设定好ASSERT:DEF:ENDIAN_CHANGE;判断ENDIAN_CHANGE是否已定义[ENDIAN_CHANGE;如果已经定义了ENDIAN_CHANGE,则(在Option.inc里已经设为FALSE)ASSERT:DEF:ENTRY_BUS_WIDTH;判断ENTRY_BUS_WIDTH是否已定义[ENTRY_BUS_WIDTH=32;如果已经定义了ENTRY_BUS_WIDTH,则判断是不是为32bChangeBigEndian;DCD0xea000007];在bigendian中,地址为A的字单元包括字节单元A,A+1,A+2,A+3,字节单元由高位到低位为A,A+1,A+2,A+3;地址为A的字单元包括半字单元A,A+2,半字单元由高位到低位为A,A+2[ENTRY_BUS_WIDTH=16andeqr14,r7,r0,lsl#20;DCD0x0007ea00也是bChangeBigEndian指令,只是由于总线不一样而取机器码];的顺序不一样,先取低位->高位上述指令是通过机器码装换而来的[ENTRY_BUS_WIDTH=8streqr0,[r0,-r10,ror#1];DCD0x070000ea也是bChangeBigEndian指令,只是由于总线不一样而取机器码];的顺序不一样|bResetHandler;我们的程序由于ENDIAN_CHANGE设成FALSE就到这儿了,转跳到复位程序入口]bHandlerUndef;handlerforUndefinedmode;0x04bHandlerSWI;handlerforSWIinterrupt;0x08bHandlerPabort;handlerforPAbort;0x0cbHandlerDabort;handlerforDAbort;0x10b.;reserved注意小圆点;0x14bHandlerIRQ;handlerforIRQinterrupt;0x18bHandlerFIQ;handlerforFIQinterrupt;0x1c;@0x20bEnterPWDN;Mustbe@0x20.;==================================================================================;下面是改变大小端的程序,这里采用直接定义机器码的方式,至说为什么这么做就得问三星了;反正我们程序里这段代码也不会去执行,不用去管它;==================================================================================;通过设置CP15的C1的位7,设置存储格式为Bigendian,三种总线方式ChangeBigEndian;//hereENTRY_BUS_WIDTH=16;@0x24[ENTRY_BUS_WIDTH=32DCD0xee110f10;0xee110f10=>mrcp15,0,r0,c1,c0,0DCD0xe3800080;0xe3800080=>orrr0,r0,#0x80;//Big-endianDCD0xee010f10;0xee010f10=>mcrp15,0,r0,c1,c0,0;对存储器控制寄存器操作,指定内存模式为Big-endian;因为刚开始CPU都是按照32位总线的指令格式运行的,如果采用其他的话,CPU别不了,必须转化;但当系统初始化好以后,则CPU能自动识别][ENTRY_BUS_WIDTH=16DCD0x0f10ee11DCD0x0080e380DCD0x0f10ee01;因为采用Big-endian模式,采用16位总线时,物理地址的高位和数据的地位对应;所以指令的机器码也相应的高低对调][ENTRY_BUS_WIDTH=8DCD0x100f11eeDCD0x800080e3DCD0x100f01ee]DCD0xffffffff;swinv0xffffffissimilarwithNOPandrunwellinbothendianmode.DCD0xffffffffDCD0xffffffffDCD0xffffffffDCD0xffffffffbResetHandler;====================================================================================;Functionforenteringpowerdownmode;1.SDRAMshouldbeinself-refreshmode.;2.AllinterruptshouldbemakskedforSDRAM/DRAMself-refresh.;3.LCDcontrollershouldbedisabledforSDRAM/DRAMself-refresh.;4.TheI-cachemayhavetobeturnedon.;5.Thelocationofthefollowingcodemayhavenottobechanged.;voidEnterPWDN(intCLKCON);EnterPWDNmovr2,r0;r2=rCLKCON保存原始数据0x4c00000c使能各模块的时钟输入tstr0,#0x8;测试bit[3]SLEEPmode?1=>sleepbneENTER_SLEEP;C=0,即TST结果非0,bit[3]=1;//进入PWDN后如果不是sleep则进入stop;//进入StopmodeENTER_STOPldrr0,=REFRESH;0x48000024DRAM/SDRAMrefreshconfigldrr3,[r0];r3=rREFRESHmovr1,r3orrr1,r1,#BIT_SELFREFRESH;EnableSDRAMself-refreshstrr1,[r0];EnableSDRAMself-refreshmovr1,#16;waituntilself-refreshisissued.maynotbeneeded.0subsr1,r1,#1bne%B0;//wait16fclksforself-refreshldrr0,=CLKCON;enterSTOPmode.strr2,[r0]movr1,#320subsr1,r1,#1;1)waituntiltheSTOPmodeisineffect.bne%B0;2)OrwaithereuntiltheCPU&Peripheralswillbeturned-off;EnteringSLEEPmode,onlytheresetbywake-upisavailable.ldrr0,=REFRESH;exitfromSDRAMselfrefreshmode.strr3,[r0]MOV_PC_LR;backtomainprocessENTER_SLEEP;NOTE.;1)rGSTATUS3shouldhavethereturnaddressafterwake-upfromSLEEPmode.ldrr0,=REFRESHldrr1,[r0];r1=rREFRESHorrr1,r1,#BIT_SELFREFRESHstrr1,[r0];EnableSDRAMself-refresh;//EnableSDRAMself-refreshmovr1,#16;Waituntilself-refreshisissued,whichmaynotbeneeded.0subsr1,r1,#1bne%B0;//Waituntilself-refreshisissued,whichmaynotbeneededldrr1,=MISCCR;IOregisterldrr0,[r1]orrr0,r0,#(7<<17);SetSCLK0=1,SCLK1=1,SCKE=1.strr0,[r1]ldrr0,=CLKCON;Entersleepmodestrr2,[r0]b.;CPUwilldiehere.;//进入SleepMode,1)设置SDRAM为self-refresh;//2)设置MISCCRbit[17]1:sclk0=sclk0:sclk0=0;//bit[18]1:sclk1=sclk0:sclk1=0;//bit[19]1:Selfrefreshretainenable;//0:Selfrefreshretaindisable;//When1,Afterwake-upfromsleep,Theself-refreshwillberetained.WAKEUP_SLEEP;ReleaseSCLKnafterwake-upfromtheSLEEPmode.ldrr1,=MISCCRldrr0,[r1]bicr0,r0,#(7<<17);SCLK0:0->SCLK,SCLK1:0->SCLK,SCKE:0->=SCKE.strr0,[r1];//设置MISCCR;Setmemorycontrolregisters;ldrr0,=SMRDATAadrlr0,SMRDATAldrr1,=BWSCON;BWSCONAddress;//总线宽度和等待控制寄存器addr2,r0,#52;EndaddressofSMRDATA0ldrr3,[r0],#4;数据处理后R0自加4,[R0]->R3,R0+4->R0strr3,[r1],#4cmpr2,r0bne%B0;//设置所有的memorycontrolregister,他的初始地址为BWSCON,初始化;//数据在以SMRDATA为起始的存储区movr1,#2560subsr1,r1,#1;1)waituntiltheSelfRefreshisreleased.bne%B0;//1)waituntiltheSelfRefreshisreleased.ldrr1,=GSTATUS3;GSTATUS3hasthestartaddressjustafterSLEEPwake-upldrr0,[r1]movpc,r0;//跳出SleepMode,进入Sleep状态前的PC;=================================================================================;如上所说,这里采用HANDLER宏去建立Hander***和Handle***之间的联系LTORG;声明文字池,因为我们用了ldr伪指令HandlerFIQHANDLERHandleFIQHandlerIRQHANDLERHandleIRQHandlerUndefHANDLERHandleUndefHandlerSWIHANDLERHandleSWIHandlerDabortHANDLERHandleDabortHandlerPabortHANDLERHandlePabort;===================================================================================;呵呵,来了来了.好戏来了,这一段程序就是用来进行第二次查表的过程了.;如果说第一次查表是由硬件来完成的,那这一次查表就是由软件来实现的了.;为什么要查两次表??;没有办法,ARM把所有的中断都归纳成一个IRQ中断异常和一个FIRQ中断异常;第一次查表主要是查出是什么异常,可我们总要知道是这个中断异常中的什么中断呀!;没办法了,再查一次表呗!;===================================================================================;//外部中断号判断,通过中断服务程序入口地址存储器的地址偏移确定;//PC=[HandleEINT0+[INTOFFSET]];H|------|;|///|;|--isr-|====>pc;L|--r8--|;|--r9--|<----spIsrIRQsubsp,sp,#4;给PC寄存器保留reservedforPCstmfdsp!,{r8-r9};把r8-r9压入栈ldrr9,=INTOFFSET;把INTOFFSET的地址装入r9INTOFFSET是一个内部的寄存器,存着中断的偏移ldrr9,[r9];I_ISRldrr8,=HandleEINT0;这就是我们第二个中断向量表的入口的,先装入r8;===================================================================================;哈哈,这查表方法够好了吧,r8(入口)+index*4(别望了一条指令是4bytes的喔),;这不就是我们要找的那一项了吗.找到了表项,下一步做什么?肯定先装入了!;==================================================================================addr8,r8,r9,lsl#2;地址对齐,因为每个中断向量占4个字节,即isr=IvectTable+Offeset*4ldrr8,[r8];装入中断服务程序的入口strr8,[sp,#8];把入口也入栈,准备用旧招ldmfdsp!,{r8-r9,pc};施招,弹出栈,哈哈,顺便把r8弹出到PC了,跳转成功!LTORG;==============================================================================;ENTRY(好了,我们的CPU要在这复位了.);==============================================================================ResetHandlerldrr0,=WTCON;1.关看门狗ldrr1,=0x0;bit[5]:0-disable;1-enable(reset默认)strr1,[r0]ldrr0,=INTMSKldrr1,=0xffffffff;2.关中断strr1,[r0]ldrr0,=INTSUBMSKldrr1,=0x7fff;3.关子中断strr1,[r0][{FALSE};4.得有些表示了,该点点LED灯了,不过被FALSE掉了.;rGPFDAT=(rGPFDAT&~(0xf<<4))|((~data&0xf)<<4);;Led_Displayldrr0,=GPFCONldrr1,=0x5500strr1,[r0]ldrr0,=GPFDATldrr1,=0x10strr1,[r0]];5.为了减少PLL的locktime,调整LOCKTIME寄存器.;ToreducePLLlocktime,adjusttheLOCKTIMEregister.ldrr0,=LOCKTIMEldrr1,=0xffffff;reset的默认值strr1,[r0];6.下面就来设置PLL了,你的板快不快就看这了!!;这里介绍一下计算公式;//Fpllo=(m*Fin)/(p*2^s);//m=MDIV+8,p=PDIV+2,s=SDIV;TheproperrangeofPandM:1<=P<=62,1<=M<=248;Fpllo必须大于200Mhz小于600Mhz;Fpllo*2^s必须小于1.2GHz;如下面的PLLCON设定中的M_DIVP_DIVS_DIV是取自option.h中;#elif(MCLK==40000000);#definePLL_M(0x48);#definePLL_P(0x3);#definePLL_S(0x2);所以m=MDIV+8=80,p=PDIV+2=5,s=SDIV=2;硬件使用晶振为10Mhz,即Fin=10Mhz;Fpllo=80*10/5*2^2=40Mhz[PLL_ON_START;Addedforconfirmclockdivide.for2440.;SettingvalueFclk:Hclk:Pclkldrr0,=CLKDIVNldrr1,=CLKDIV_VAL;0=1:1:1,1=1:1:2,2=1:2:2,3=1:2:4,4=1:4:4,5=1:4:8,;6=1:3:3,7=1:3:6.option.inc中定义CLKDIV_VAL=7strr1,[r0];//数据表示分频数;===============================================================================;MMU_SetAsyncBusMode和MMU_SetFastBusMode都在4K代码以上,;如果你想你编译出来的程序能在NAND上运行的话,就不要在这调用这两函数了.;如果你不要求的话,你就用把.啥事没有.;为什么是4K,问三星吧,就提供4K的内部SRAM,要是提供400K多好呀.;好了,好了,4K就4K吧,不能用这两函数,自己写还不行吗,下面的代码这这么来了,;实现和上面两函数一样的功能.;===============================================================================;[CLKDIV_VAL>1;意思是Fclk:Hclk不是1:1.;blMMU_SetAsyncBusMode;|;blMMU_SetFastBusMode;defaultvalue.;];==手册第243页==;IfHDIVNisnot0,theCPUbusmodehastobechangedfromthefastbusmodetotheasynchronous;busmodeusingfollowinginstructions;MMU_SetAsyncBusMode;mrcp15,0,r0,c1,c0,0;orrr0,r0,#R1_nF:OR:R1_iA;mcrp15,0,r0,c1,c0,0[CLKDIV_VAL>1;意思是Fclk:Hclk不是1:1.mrcp15,0,r0,c1,c0,0orrr0,r0,#0xc0000000;R1_nF:OR:R1_iAmcrp15,0,r0,c1,c0,0|mrcp15,0,r0,c1,c0,0bicr0,r0,#0xc0000000;R1_iA:OR:R1_nFmcrp15,0,r0,c1,c0,0];配置UPLL;//ConfigureUPLLFin=12.0MHzUFout=48MHzldrr0,=UPLLCONldrr1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV);//USBPLLCONFIG56,2,2===>48MHzstrr1,[r0];7个nop必不可少!!nop;//Caution:AfterUPLLsetting,atleast7-clocksdelaymustbeinsertedforsettingnop;hardwarebecompleted.nopnopnopnopnop;配置MPLL;//ConfigureMPLLFin=12.0MHzMFout=304.8MHzldrr0,=MPLLCONldrr1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV);68,1,1==>304MHzstrr1,[r0]];检查是否从SLEEP模式中恢复;//Checkifthebootiscausedbythewake-upfromSLEEPmode.ldrr1,=GSTATUS2ldrr0,[r1]tstr0,#0x2;testifbit[1]is1or00->C=1;1->C=0;Incaseofthewake-upfromSLEEPmode,gotoSLEEP_WAKEUPhandler.bneWAKEUP_SLEEP;C=0,jumpEXPORTStartPointAfterSleepWakeUpStartPointAfterSleepWakeUp;===============================================================================;设置内存控制器等寄存器的值,因为这些寄存器是连续排列的,所以采用如下办法对这些;寄存器进行连续设置.其中用到了SMRDATA的数据,这在代码后面有定义;===============================================================================;这是设置SDRAM,flashROM存储器连接和工作时序的程序,片选定义的程序;SMRDATAmap在下面的程序中定义;SMRDATA中涉及的值请参考memcfg.inc程序;Setmemorycontrolregisters;ldrr0,=SMRDATA;dangerous!!!adrlr0,SMRDATA;becareful!,tinkoldrr1,=BWSCON;BWSCONAddressaddr2,r0,#52;EndaddressofSMRDATA;SMRDATA数据的结束地址,共有52字节的数据0ldrr3,[r0],#4strr3,[r1],#4cmpr2,r0bne%B0;%表示搜索,B表示反向-back(F表示向前-forward),0为局部标号(0~99);================================================================================;如果EINT0产生(这中断就是我们按键产生的),就清除SDRAM,不过好像没人会在这个时候按;================================================================================;checkifEIN0buttonispressedldrr0,=GPFCONldrr1,=0x0;00=Inputstrr1,[r0]ldrr0,=GPFUPldrr1,=0xff;1-Thepullupfunctionisdisabled.strr1,[r0]ldrr1,=GPFDATldrr0,[r1]bicr0,r0,#(0x1e<<1);bitcleartstr0,#0x1bne%F1;如果没有按,就跳到后面的1标号处=>Initializestacks;这就是清零内存的代码ldrr0,=GPFCONldrr1,=0x55aastrr1,[r0];ldrr0,=GPFUP;ldrr1,=0xff;strr1,[r0]ldrr0,=GPFDATldrr1,=0x0strr1,[r0];LED=****movr1,#0movr2,#0movr3,#0movr4,#0movr5,#0movr6,#0movr7,#0movr8,#0ldrr9,=0x4000000;64MBldrr0,=0x300000000stmiar0!,{r1-r8}subsr9,r9,#32bne%B0;到这就结束了.;//4.初始化各模式下的栈指针;Initializestacks1blInitStacks;=========================================================================;哈哈,下面又有看头了,这个初始化程序好像被名曰hzh的高手改过;能在NORNAND还有内存中运行,当然了,在内存中运行最简单了.;在NORNAND中运行的话都要先把自己拷到内存中.;此外,还记得上面提到的|Image$$RO$$Base|,|Image$$RO$$Limit|...吗?;这就是拷贝的依据了!!!;=========================================================================;BWSCON的[2:1]反映了外部引脚OM[1:0]:若OM[1:0]!=00,从NORFLash启动或直接在内存运行;;若OM[1:0]==00,则为NandFlashModeldrr0,=BWSCONldrr0,[r0]andsr0,r0,#6;#6==0110-->BWSCON[2:1]bnecopy_proc_beg;OM[1:0]!=00,NORFLashboot,不读取NANDFLASHadrr0,ResetEntry;否则,OM[1:0]==0,为从NANDFLash启动cmpr0,#0;再比较入口是否为0地址处;如果是0才是真正从NAND启动,因为其4k被复制到0地址开始的stepingstone内部sram中;注意adr得到的是相对地址,非绝对地址==ifuseMulti-ice,bnecopy_proc_beg;如果!=0,说明在usingice,这种情况也不读取NANDFLASH.;don'treadnandflashforboot;nop;==============这一段代码完成从NANDFlash读代码到RAM=====================nand_boot_beg;movr5,#NFCONF;首先设定NAND的一些控制寄存器;settimingvalueldrr0,=(7<<12)|(7<<8)|(7<<4)strr0,[r5];enablecontrolldrr0,=(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)strr0,[r5,#4]blReadNandID;按着读取NAND的ID号,结果保存在r5里movr6,#0;r6设初值0.ldrr0,=0xec73;期望的NANDID号cmpr5,r0;这里进行比较beq%F1;相等的话就跳到下一个1标号处ldrr0,=0xec75;这是另一个期望值cmpr5,r0beq%F1;相等的话就跳到下一个1标号处movr6,#1;不相等,设置r6=1.1blReadNandStatus;读取NAND状态,结果放在r1里movr8,#0;r8设初值0,意义为页号ldrr9,=ResetEntry;r9设初值为初始化程序入口地址;注意,在这里使用的是ldr伪指令,而不是上面用的adr伪指令,它加载的是ResetEntry;的绝对地址,也就是我们期望的RAM中的地址,在这里,它和|Image$$RO$$Base|一样;也就是说,我如我们编译程序时RObase指定的地址在RAM里,而把生成的文件拷到;NAND里运行,由ldr加载的r9的值还是定位在内存.???2andsr0,r8,#0x1f;凡r8为0x1f(32)的整数倍-1,eq有效,ne无效bne%F3;这句的意思是对每个块(32页)进行检错--在每个块的开始页进行movr0,r8;r8->r0blCheckBadBlk;检查NAND的坏区cmpr0,#0;比较r0和0addner8,r8,#32;存在坏块的话就跳过这个坏块:+32得到下一块.;故:r8=blockpageaddr,因为读写是按页进行的(每页512Byte)bne%F4;然后跳到4进行循环条件判断。没有的话就跳到标号3处copy当前页3movr0,r8;当前页号->r0movr1,r9;当前目标地址->r1blReadNandPage;读取该页的NAND数据到RAMaddr9,r9,#512;每一页的大小是512Bytesaddr8,r8,#1;r8指向下一页4cmpr8,#256;比较是否读完256页即128KBytes;注意:这说明此程序默认拷贝128KByte的代码(byTinko)bcc%B2;如果r8小于256(没读完),就返回前面的标号2处;nowcopycompletedmovr5,#NFCONF;DisableNandFlashldrr0,[r5,#4]bicr0,r0,#1strr0,[r5,#4]ldrpc,=copy_proc_beg;调用copy_proc_beg;个人认为应该为InitRam?????????????;===========================================================copy_proc_begadrlr0,ResetEntry;ResetEntry值->r0;这里应该注意,使用的是adr,而不是ldr。使用ldr说明ResetEntry是个绝对地址,这个地址是在程序;链接的时候确定的。而使用adr则说明ResetEntry的地址和当前代码的执行位置有关,它是一个相对的;地址。比如这段代码在stepingstone里面执行,那么ResetEntry的地址就是零。如果在RAM里执行,那;么ResetEntry就应是RAM的一个地址,应该等于RObase。ldrr2,BaseOfROM;BaseOfROM值(后面有定义)->r2cmpr0,r2;比较ResetEntry和BaseOfROMldreqr0,TopOfROM;如果相等的话(在内存运行---ice--无需复制code区中的ro段,;但需要复制code区中的rw段),TopOfROM->r0beqInitRam;同时跳到InitRam;否则,下面开始复制code的RO段;============================================================;下面这个是针对代码在NORFLASH时的拷贝方法;功能为把从ResetEntry起,TopOfROM-BaseOfROM大小的数据拷到BaseOfROM;TopOfROM和BaseOfROM为|Image$$RO$$Limit|和|Image$$RO$$Base|;|Image$$RO$$Limit|和|Image$$RO$$Base|由连接器生成;为生成的代码的代码段运行时的起启和终止地址;BaseOfBSS和BaseOfZero为|Image$$RW$$Base|和|Image$$ZI$$Base|;|Image$$RW$$Base|和|Image$$ZI$$Base|也是由连接器生成;两者之间就是初始化数据的存放地;--在加载阶段,不存在ZI区域--;=============================================================ldrr3,TopOfROM0ldmiar0!,{r4-r7};开始时,r0=ResetEntry---sourcestmiar2!,{r4-r7};开始时,r2=BaseOfROM---destinationcmpr2,r3;终止条件:复制了TopOfROM-BaseOfROM大小bcc%B0;---------------------------------------------------------------;下面2行,根据理解,由tinko添加;猜测上面的代码不应该用"!",以至于地址被修改。这里重新赋值;---------------------------------------------------------------adrlr0,ResetEntry;don'tuseadr,'causeoutofrangeerroroccuresldrr2,BaseOfROM;旨在计算出正确的RW区起始位置;下面2行目的是为了计算正确的r0(必须使之指向code区中的rw域开始处)subr2,r2,r3;r2=BaseOfROM-TopOfROM=(-)代码长度subr0,r0,r2;r0=ResetEntry-(-)代码长度=ResetEntry+代码长度InitRam;复制代码加载位置中的RM区到|Image$$RW$$Base|ldrr2,BaseOfBSS;BaseOfBSS->r2,BaseOfBSS=|Image$$RW$$Base|ldrr3,BaseOfZero;BaseOfZero->r3,BaseOfZero=|Image$$ZI$$Base|0cmpr2,r3;比较BaseOfBSS和BaseOfZeroldrccr1,[r0],#4;当代码在内存中运行时,r0(初始值)=TopOfROM.;这之后的BaseOfZero-BaseOfBSS仍属于code,需拷贝到BaseOfBSSstrccr1,[r2],#4bcc%B0;用0初始化ZI区movr0,#0ldrr3,EndOfBSS;EndOfBSS=|Image$$ZI$$Limit|1cmpr2,r3strccr0,[r2],#4bcc%B1;要是r21;meansFclk:Hclkisnot1:1.;blMMU_SetAsyncBusMode;|;blMMU_SetFastBusMode;defaultvalue.;];blLed_Test;===========================================================;进入C语言前的最后一步了,就是把我们用说查二级向量表;的中断例程安装到一级向量表(异常向量表)里.;//5.设置缺省中断处理函数;SetupIRQhandlerldrr0,=HandleIRQ;Thisroutineisneededldrr1,=IsrIRQ;ifthereisn't'subspc,lr,#4'at0x18,0x1cstrr1,[r0];//initializetheIRQ将普通中断判断程序的入口地址给HandleIRQ;/////////////////////////////////////////////////////////;注意,以下这段可能不需要!!!!!!!!!!!!!!!!!!;//6.将数据段拷贝到ram中将零初始化数据段清零跳入C语言的main函数执行到这步结束bootloader初步引导结束;Ifmain()isused,thevariableinitializationwillbedonein__main().[{FALSE};bytinko--最外面的条件由tinko添加,实际上不再执行这段[:LNOT:USE_MAIN;initialized{FALSE};CopyandpasteRWdata/zeroinitializeddataLDRr0,=|Image$$RO$$Limit|;GetpointertoROMdataLDRr1,=|Image$$RW$$Base|;andRAMcopyLDRr3,=|Image$$ZI$$Base|;Zeroinitbase=>topofinitialiseddataCMPr0,r1;Checkthattheyaredifferentjustfordebug????????BEQ%F21CMPr1,r3;CopyinitdataLDRCCr2,[r0],#4;-->LDRCCr2,[r0]+ADDr0,r0,#4STRCCr2,[r1],#4;-->STRCCr2,[r1]+ADDr1,r1,#4BCC%B12LDRr1,=|Image$$ZI$$Limit|;TopofzeroinitsegmentMOVr2,#03CMPr3,r1;ZeroinitSTRCCr2,[r3],#4BCC%B3]];!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;***************************************;bytinko[{TRUE};得有些表示了,该点点LED灯了;rGPFDAT=(rGPFDAT&~(0xf<<4))|((~data&0xf)<<4);;Led_Displayldrr0,=GPFCONldrr1,=0x5500strr1,[r0]ldrr0,=GPFDATldrr1,=0xe0strr1,[r0]ldrr2,=0xffffffff;1subr2,r2,#1bne%b1ldrr0,=GPFDATldrr1,=0xe0;b.;diehere];***************************************;*****************************************************************************;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;妈呀,终说见到艳阳天了!!!!!!!!!!;跳到C语言的main函数处了.;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!;*****************************************************************************[:LNOT:THUMBCODE;ifthumbcode={false}blmainL代表logic变量blMain;Don'tusemain()because......b.;注意小圆点];//ifthumbcod={ture}[THUMBCODE;forstart-upcodeforThumbmodeorrlr,pc,#1bxlrCODE16blMain;Don'tusemain()because......b.;注意小圆点CODE32];functioninitializingstacksInitStacks;Don'tuseDRAM,suchasstmfd,ldmfd......;SVCstackisinitializedbefore;Undertoolkitver2.5,'msrcpsr,r1'canbeusedinsteadof'msrcpsr_cxsf,r1'mrsr0,cpsrbicr0,r0,#MODEMASKorrr1,r0,#UNDEFMODE|NOINTmsrcpsr_cxsf,r1;UndefModeldrsp,=UndefStack;UndefStack=0x33FF_5C00orrr1,r0,#ABORTMODE|NOINTmsrcpsr_cxsf,r1;AbortModeldrsp,=AbortStack;AbortStack=0x33FF_6000orrr1,r0,#IRQMODE|NOINTmsrcpsr_cxsf,r1;IRQModeldrsp,=IRQStack;IRQStack=0x33FF_7000orrr1,r0,#FIQMODE|NOINTmsrcpsr_cxsf,r1;FIQModeldrsp,=FIQStack;FIQStack=0x33FF_8000bicr0,r0,#MODEMASK|NOINTorrr1,r0,#SVCMODEmsrcpsr_cxsf,r1;SVCModeldrsp,=SVCStack;SVCStack=0x33FF_5800;USERmodehasnotbeinitialized.;//为什么不用初始化user的stacks,系统刚启动的时候运行在哪个模式下?movpc,lr;TheLRregisterwon'tbevalidifthecurrentmodeisnotSVCmode.?;//系统一开始运行就是SVCmode?;===========================================================ReadNandIDmovr7,#NFCONFldrr0,[r7,#4];NFChipEn();bicr0,r0,#2strr0,[r7,#4]movr0,#0x90;WrNFCmd(RdIDCMD);strbr0,[r7,#8]movr4,#0;WrNFAddr(0);strbr4,[r7,#0xc]1;while(NFIsBusy());ldrr0,[r7,#0x20]tstr0,#1beq%B1ldrbr0,[r7,#0x10];id=RdNFDat()<<8;movr0,r0,lsl#8ldrbr1,[r7,#0x10];id|=RdNFDat();orrr5,r1,r0ldrr0,[r7,#4];NFChipDs();orrr0,r0,#2strr0,[r7,#4]movpc,lrReadNandStatusmovr7,#NFCONFldrr0,[r7,#4];NFChipEn();bicr0,r0,#2strr0,[r7,#4]movr0,#0x70;WrNFCmd(QUERYCMD);strbr0,[r7,#8]ldrbr1,[r7,#0x10];r1=RdNFDat();ldrr0,[r7,#4];NFChipDs();orrr0,r0,#2strr0,[r7,#4]movpc,lrWaitNandBusymovr0,#0x70;WrNFCmd(QUERYCMD);movr1,#NFCONFstrbr0,[r1,#8]1;while(!(RdNFDat()&0x40));ldrbr0,[r1,#0x10]tstr0,#0x40beq%B1movr0,#0;WrNFCmd(READCMD0);strbr0,[r1,#8]movpc,lrCheckBadBlkmovr7,lrmovr5,#NFCONFbicr0,r0,#0x1f;addr&=~0x1f;ldrr1,[r5,#4];NFChipEn()bicr1,r1,#2strr1,[r5,#4]movr1,#0x50;WrNFCmd(READCMD2)strbr1,[r5,#8]movr1,#5;6;6->5strbr1,[r5,#0xc];WrNFAddr(5);(6)6->5strbr0,[r5,#0xc];WrNFAddr(addr)movr1,r0,lsr#8;WrNFAddr(addr>>8)strbr1,[r5,#0xc]cmpr6,#0;if(NandAddr)movner0,r0,lsr#16;WrNFAddr(addr>>16)strnebr0,[r5,#0xc];blWaitNandBusy;WaitNFBusy();donotuseWaitNandBusy,afterWaitNandBusywillreadpartA!movr0,#1001subsr0,r0,#1bne%B12ldrr0,[r5,#0x20]tstr0,#1beq%B2ldrbr0,[r5,#0x10];RdNFDat()subr0,r0,#0xffmovr1,#0;WrNFCmd(READCMD0)strbr1,[r5,#8]ldrr1,[r5,#4];NFChipDs()orrr1,r1,#2strr1,[r5,#4]movpc,r7ReadNandPagemovr7,lrmovr4,r1movr5,#NFCONFldrr1,[r5,#4];NFChipEn()bicr1,r1,#2strr1,[r5,#4]movr1,#0;WrNFCmd(READCMD0)strbr1,[r5,#8]strbr1,[r5,#0xc];WrNFAddr(0)strbr0,[r5,#0xc];WrNFAddr(addr)movr1,r0,lsr#8;WrNFAddr(addr>>8)strbr1,[r5,#0xc]cmpr6,#0;if(NandAddr)movner0,r0,lsr#16;WrNFAddr(addr>>16)strnebr0,[r5,#0xc]ldrr0,[r5,#4];InitEcc()orrr0,r0,#0x10strr0,[r5,#4]blWaitNandBusy;WaitNFBusy()movr0,#0;for(i=0;i<512;i++)1ldrbr1,[r5,#0x10];buf[i]=RdNFDat()strbr1,[r4,r0]addr0,r0,#1bicr0,r0,#0x10000cmpr0,#0x200bcc%B1ldrr0,[r5,#4];NFChipDs()orrr0,r0,#2strr0,[r5,#4]movpc,r7;--------------------LEDtestEXPORTLed_TestLed_Testmovr0,#0x56000000movr1,#0x5500strr1,[r0,#0x50]0movr1,#0x50strr1,[r0,#0x54]movr2,#0x1000001subsr2,r2,#1bne%B1movr1,#0xa0strr1,[r0,#0x54]movr2,#0x1000002subsr2,r2,#1bne%B2b%B0movpc,lr;===========================================================;=====================================================================;Clockdivisiontest;Assemblecode,becauseVSYNCtimeisveryshort;=====================================================================EXPORTCLKDIV124EXPORTCLKDIV144CLKDIV124ldrr0,=CLKDIVNldrr1,=0x3;0x3=1:2:4strr1,[r0];waituntilclockisstablenopnopnopnopnopldrr0,=REFRESHldrr1,[r0]bicr1,r1,#0xffbicr1,r1,#(0x7<<8)orrr1,r1,#0x470;REFCNT135strr1,[r0]nopnopnopnopnopmovpc,lrCLKDIV144ldrr0,=CLKDIVNldrr1,=0x4;0x4=1:4:4strr1,[r0];waituntilclockisstablenopnopnopnopnopldrr0,=REFRESHldrr1,[r0]bicr1,r1,#0xffbicr1,r1,#(0x7<<8)orrr1,r1,#0x630;REFCNT675-1520strr1,[r0]nopnopnopnopnopmovpc,lr;存储器控制寄存器的定义区LTORGSMRDATADATA;Memoryconfigurationshouldbeoptimizedforbestperformance;Thefollowingparameterisnotoptimized.;Memoryaccesscycleparameterstrategy;1)ThememorysettingsissafeparametersevenatHCLK=75Mhz.;2)SDRAMrefreshperiodisforHCLK<=75Mhz.DCD(0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28));各bank的buswidth;没有B0,因为由OM[1:0]pins确定DCD((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC));GCS0DCD((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC));GCS1DCD((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC));GCS2DCD((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC));GCS3DCD((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC));GCS4DCD((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC));GCS5DCD((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN));GCS6B6_MT定义在memcfg.inc中,11-->SDRAM;B6_SCAN-非reset默认值DCD((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN));GCS7DCD((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT);Tchr-notused;DCD0x32;SCLKpowersavingmode,BANKSIZE128M/128MDCD0x31;SCLKpowersavingmode,BANKSIZE64M/64MDCD0x30;MRSR6CL=3clkDCD0x30;MRSR7CL=3clkBaseOfROMDCD|Image$$RO$$Base|TopOfROMDCD|Image$$RO$$Limit|BaseOfBSSDCD|Image$$RW$$Base|BaseOfZeroDCD|Image$$ZI$$Base|EndOfBSSDCD|Image$$ZI$$Limit|ALIGNAREARamData,DATA,READWRITE^_ISR_STARTADDRESS;_ISR_STARTADDRESS=0x33FF_FF00HandleReset#4HandleUndef#4HandleSWI#4HandlePabort#4HandleDabort#4HandleReserved#4HandleIRQ#4HandleFIQ#4;Don'tusethelabel'IntVectorTable',;ThevalueofIntVectorTableisdifferentwiththeaddressyouthinkitmaybe.;IntVectorTable;@0x33FF_FF20HandleEINT0#4HandleEINT1#4HandleEINT2#4HandleEINT3#4HandleEINT4_7#4HandleEINT8_23#4HandleCAM#4;Addedfor2440.HandleBATFLT#4HandleTICK#4HandleWDT#4HandleTIMER0#4HandleTIMER1#4HandleTIMER2#4HandleTIMER3#4HandleTIMER4#4HandleUART2#4;@0x33FF_FF60HandleLCD#4HandleDMA0#4HandleDMA1#4HandleDMA2#4HandleDMA3#4HandleMMC#4HandleSPI0#4HandleUART1#4HandleNFCON#4;Addedfor2440.HandleUSBD#4HandleUSBH#4HandleIIC#4HandleUART0#4HandleSPI1#4HandleRTC#4HandleADC#4;@0x33FF_FFA0END