试图搞懂MDK程序下载到Flash(一)--Nand Flash启动流程,加载域和
NAND FLASH启动流程??? 在这里我先以TQ2440的启动代码分析,因为手上有本书,反正Nand Flash启动流程都是一样的对于mini2440和TQ2440来说。TQ2440的启动代码部分如下: ??? ;********************************************************************************** ??? .....???????????????????????????????????????????????;38行之前的代码都是一些准备工作 ??? .....?????????????????????????????????????????????? ;如宏定义、符号导入之类的 ??? 35?????????????? AREA???? Init,CODE,READONLY ????36?????????????? ENTRY ??? 37????? ResetEntry ??? 38??????????????? b???????????????ResetHandled????????? ;程序执行代码的第一句? ??? ....????????????????????????????????????????????????????????????????? ;跳转到标号ResetHandler处执行 ??? .... ??? 63?????? ResetHandler ??? 64??????????????? ldr???????????? r0,=WTCON ??? 65?????????????? ldr????????????? r0,=0x0 ??? ..... ??? ..... ??? 106??????????? ? ldr??????????? ?r0,=BWSCON ??? 107?????????? ?? ldr???????????? r0,[r0] ????108??????????? ? ands???????? r0,r0,#6????????????????????? ;OM[1:0]!=0,NOR Flash 启动 ????109?????????? ?? bne????????? copy_proc_beg?????????? ;do not read nand flash ??? 110?????????? ? adr???????????r0,ResetEntry????????????? ;OM[1:0]==0,NAND Flash启动 ??? 111???????? ??? cmp????????? r0,#0?????????????????????????? ;if use Multi-ice ??? 112??????????? ?bne?????????? copy_proc_beg??????????? ;do not read nand flash for boot ??? 113??? nand_boot_beg ??? 114?????????????bl??????????? RdNF2SDRAM ????115???????????? ldr?????????? pc,=copy_proc_beg ??? 116????copy_proc_beg ????117???????????? adr???????? r0,ResetEntry ??? 118???????????? ldr????????? r2,BaSEOfROM ??? 119???????????? cmp?????? r0,r2 ????120???????????? ldreq????? r0,TopOfRom ??? 121???????????? beq??????? InitRam ????122???????????? ldr????????? r3,TopOfRom ??? 123??? 0? ??? 124???????????? ldmia????? r0!,{r4-r7} ??? 125???????????? stmia????? r2!,{r4-r7} ????126???????????? cmp???????r2,r3 ??? 127?????????? ? bcc???????? %B0 ? ??? 128???????????? sub??????? r2,r2,r3 ??? 129???????????? sub??????? r0,r2? ??? 130??? InitRam ????131??????????? ldr???????????r2,BaSEOfBSS ??? 132??????????? ldr?????????? r3,BaSEOfZero ????133??? 0 ????134???????????cmp??????? r2,r3 ????135?????????? ldrcc??????? r1,[r0],#4 ????136?????????? strcc?????? r1,[r2],#4 ????137???????? ? bcc???????? %B0 ??? 138????????? ?mov??????? r0,#0 ???? 139??????? ? ldr????????? r3,EndOfBSS ???? 140??? 1 ???? 141????????? cmp?????? r2,r3 ???? 142????????? strcc?????? r0,#4 ????? 143???????? bcc????????%B1 ????? ;安装中断向量表 ????? 144???????? ldr???????? r0,=HandleIRQ ????? 145???????? ldr???????? r1,=IsrIRQ ??????146??????? ?str????????? r1,[r0] ? ??????147?????? ? b?????????? Main ??????148?? InitStacks ??????149??????????????????????? ;堆栈初始化代码部分的开始行 ?????? .... ????? 168??????????????????????? ;堆栈初始化代码部分的结束行? ???????.... ???????.... ????? 231?????????END?????? ;启动代码结束 ???? 现在开始分析代码: ??? 106????????????? ldr???????????? r0,=BWSCON?????????????????????????????????????????????????????? ??? ? ??? 107????????????? ldr???????????? r0,[r0]????????????????????????????????????????????????????????????? ???????? ?? ??? 108????????????? ands???????? r0,#6????????????????????? ;OM[1:0]!=0,NOR Flash 启动 ??? 109????????????? bne????????? copy_proc_beg?????????? ;do not read nand flash??????????? ??? 110???????????? adr?????????? r0,ResetEntry????????????? ;OM[1:0]==0,NAND Flash启动 ??? 111???????????? cmp????????? r0,#0?????????????????????????? ;if use Multi-ice??????????????????????????? ??? 112???????????? bne?????????? copy_proc_beg??????????? ;do not read nand flash for boot ?????第106~112行才是选择从NAND FLASH或者NOR FLASH启动的关键。 ???? 第106~107行将BWSCON寄存器里的数据读入寄存器r0。从S3C2440的数据手册上可以查到BWSCON寄存器第1、2位,反映了系统总线宽度的信息。 ???? 第108行,and指令主要用于测试数据的某几位是0还是1,后面的s表示该指令的运算结果影响标志位。因此,该条指令主要是测试r0第1、2两位。当运算结果是0时,说明r0的第1、2位是0,即BWSCON寄存器的第1、2位是0,即系统是从Nand Flash启动的,则执行第110行程序;当运算结果不为0时,说明是从Nor Flash启动,在执行第109行程序。 ??? 第109行,执行该条指令的前提是系统从Nor Flash启动,然后跳转到copy_proc_beg标号处执行,其功能是:实现代码从加载域到运行域的搬移工作,这也是启动代码的核心。关于加载域和运行域在后面会讲到。 ???第110~112行,其中adr指令比较难理解,adr伪指令是将标号基于PC的相对地址加载到寄存器中,因为是将程序下载到了NADN FLASH的0地址处,所以ResetEntry的地址为0,所以最终结果就是r0=0。关于ldr与adr指令、相对地址与绝对地址请看博客:点此打开链接 ?? 第112行,由上面的分析可以知道,该行指令不执行,执行第113行指令。 ??? 113??? nand_boot_beg???????????????????????????????????????????? ??? 114???????????? bl??????????? RdNF2SDRAM????????????????????? ??? 115???????????? ldr?????????? pc,=copy_proc_beg?????????????? ???? 第113行,只是一个程序标号,没有实际的用处。 ???? 第114行,调用C语言函数RdNF2SDRAM,将代码从NAND FLASH读入到内存中,这个函数是在C文件中定义的。 ???? 第115行,用ldr指令将copy_proc_beg的绝对地址加载到程序计数器PC中,即跳转到了内存中去执行。 ???? 到此为止,从NAND FLASH启动的流程已经完全讲清楚了,尽管我还是不”很清楚“,下面就结合图1详细分析从NAND FLASH启动总流程,做到真清楚! ????① 系统上电后,S3C2440处理器通过硬件电路自动将NAND FLASH中的前4KB代码复制到内部的Stepping Stonezhong ,该Stepping Stone就是一块RAM,可以执行程序。 ???? ② 程序从Stepping Stone的0地址处开始执行第1条指令。 ???? ③ 程序执行到bl???? RdNF2SDRAM(第114行)时,调用NAND FLASH函数,将NAND FLASH中的代码全部复制到SDRAM中。 ???? ④ 执行? ldr??? pc,=copy_proc_beg,将copy_proc_beg的绝对地址加载到程序计数器PC中。 ???? ⑤ 程序跳转到SDRAM中copy_proc_beg标号处执行。 ???? 注意:第(4)步中提到了copy_proc_beg的绝对地址,那么什么是绝对地址呢?绝对地址是程序编译链接后确定的,如图2所示 在链接时,指定的entry 地址是0x3000 0000,这个entry地址即第36行的ENTRY,也就是说,在最终编译链接后生成的可执行程序中,copy_proc_beg的绝对地址=0x3000 0000+偏移量。这个偏移量是多少呢?就是copy_proc_beg距离ENTRY的偏移量。因此,在第(4)步执行完后,虽然stepping stone中也有copy_proc_beg,SDRAM中也有copy_proc_beg,但是程序并没有跳到stepping stone中的copy_proc_beg处执行,而是跳到了SDRAM中的copy_proc_beg处去执行。 加载域和运行域、RW、RO、ZI ?? 一个简单的可执行程序的映像文件如图3所示。它由RO、RW、和ZI三个段组成,其中RO为代码段和只读数据段;RW为可读/写的数据段;ZI为未初始化的数据段。 ?? 此外,映像文件还可以分为加载域(Load View)和运行域(Execution View)。加载域反映了ARM可执行映像文件各个段存放在存储器中时的位置关系,运行域反映了ARM可执行映像文件各个段真正执行时在存储器中的位置关系。 ?? 前文讲到,从NOR FLASH启动后,当启动代码执行到第109行时,会跳转到copy_proc_beg处执行。此时,程序加载域和运行域的分布情况如图4所示 ????? 加载域的起始地址是0x0000 0000,为什么呢?因为加载域反映了ARM可执行文件存放在存储器中时的位置关系,程序下载到NOR FLASH中,又知道NOR FLASH的起始地址是0x0000 0000,因此加载域的起始地址是0x0000 0000,在加载域中,首先存放RO,后面紧跟着存放的是RW,并没有ZI。RO为代码段,属性为只读;RW为已经初始化的全局变量段,属性可读、可写;ZI为未初始化的全局变量段。为什么在加载域中没有ZI呢,这主要是为了减小ARM可执行映像文件的容量,因为ZI中存放的是未初始化的全局变量,那么只需要记录该段的容量即可,在运行域中需要根据这个容量分配内存,然后用0填充。 ????? 运行域的起始地址是0x3000 0000,这又是为什么呢? ???? 如图4所示,为ADS中在配置选项”ARM Linker“中有一项是”RO Base“,这个地方就表明了运行域中RO的起始地址。"RW Base"为空,这表明在运行域总共RW紧跟着RO的后面。 ???????????????????????????????????????????????????????????????? 启动代码做的工作就是如图3中虚线部分所指示的,将各个段搬移到指定的位置,然后将ZI初始化为0.下面这段程序就是围绕这个工作来展开。 ??? 116??? copy_proc_beg?????????????????????????????? ??? 117???????????? adr???????? r0,ResetEntry?????????? ??? 118???????????? ldr????????? r2,BaSEOfROM???????? ??? 119???????????? cmp?????? r0,r2????????????????????????? ??? 120???????????? ldreq????? r0,TopOfRom?????????? ??? 121???????????? beq??????? InitRam???????????????????? ??? 122???????????? ldr????????? r3,TopOfRom??????????? ??? 123??? 0?????????????????????????????????????????????????????? ??? 124???????????? ldmia????? r0!,{r4-r7}?????????????????? ??? 125???????????? stmia????? r2!,192)">??? 126???????????? cmp?????? r2,r3?????????????????????????? ??? 127???????????? bcc???????? %B0?????????????????????????
??? 128???????????? sub??????? r2,r3?????????????????????? ??? 129???????????? sub??????? r0,r2?????????????????????? ?????有了上面的讲解,再看第116~129行程序就很容易理解。在上述程序中,BaSEOfROM和TopOfROM是用DCD分配的内存单元,里面存放的是RO的起始地址和结束地址(准确点说是结束地址+1)。该起始地址和结束地址是由编译器自动生成的,在程序中使用即可,见第183~184行 ???? 183????????? BaSEOfROM????? DCD??????? |Image$$RO$$Base|????? ???? 184????????? TopOfROM????? ? DCD??????? |Image$$RO$$Limit|?????? ?? 其中|Image$$RO$$Base|???和|Image$$RO$$Base|? 是ADS编译器自动生成的符号,在该启动代码的开头使用IMPORT引入了,如下 ?????28??????????? IMPORT??? |Image$$RO$$Base|???????????????? ;Base of ROM?code ? ?????? 29?????????? IMPORT??? |Image$$RO$$Limit|????????????????? ;End? of?? ROM? code??dd????????????????????????? ?????
??? 下面接着分析第116~129行。 ??? 第117行,使用adr指令将ResetEntry的相对地址加载到寄存器r0中,这个地址就是0。 ??? 第118行,使用ldr指令将RO的起始地址加载到寄存器r2中。 ??? 第119行,比较r0和r2是否相同,如图5所示,可以看出r0和r2并不相等,因此第120~121行并不执行。 ??? 第122行,使用ldr指令将RO的结束地址+1加载到寄存器r3中。 ??? 执行完上述指令后,图5详细展示程序执行的最后结果,下面的工作就是将代码从r0指示的加载域中的RO搬移到r2、r3所限定的运行域中的RO。 ??? 第123行,定义了一个局部标号0。 ? ? 第124行,使用批量加载指令ldmia,将r0地址出的数据加载到寄存器r4~r7中,注意后面的感叹号“!”说明取完数据后,r0的地址自动更新,即指向下一个地址处。此外,可以得出这样的结论每次搬移16个字节(每个寄存器是4个字节的长度,共4个寄存器) 。 ??? 第125行,将r4~r7中的数据存储到r2开始的地址处,同时r2地址自动更新。 ??? 第126~127行,比较r2和r3的值,如果r2的值小于r3的值,就跳转到第123行定义的局部标号0处知心。更形象的理解是,如图5所示,当r2小于r3时,说明RO还没有搬移完,因此就接着搬移,直到r2的值大于r3的值。 ??? 第128~129行,这两行的作用就是调整r0的值,使其指向加载域中RW的起始地址。有的人会说,怎么r0就指向RW起始地址呢?已知咱们的RW段紧跟着RO段,当搬移完RO内容后,很可能r2不一定等于r3,如果r2大于r3,说明r2指向的地址超过了运行域中的RW段起始地址,此时r0指向的地址也超过了加载域中RW段的起始地址。为了更形象的展示这一调整的过程,请看图6: ????? 由第124行的讨论可知,每次搬移16个字节,因此最后肯那个出现的情况如图6所示,当r2的值大于r3的值时,停止搬移,那么现在的情况是已经完成了RO从加载域到运行域的搬移,下面需要完成RW从加载域到运行域的搬移。面临的首要问题是如何在加载域中找到RW的起始地址,现在知道的信息是在加载域中RW紧邻着RO存放。由图6可以看到r0已经移到了RW,只要计算出这个偏移,即可计算出RW的首地址。这个偏移地址怎么计算呢,其实就是r2-r3。 ???? 第128行,计算出偏移地址r2-r3,将其存放在寄存器r2中。 ???? 第129行,将r0的值减去这个偏移地址,即得到了RW的起始地址。 ??? 其实,在这种加载域和运行域布局情况下,即加载域和运行域中RO和RW紧挨着存放,完全可以将RO和RW整体搬移,但是该启动代码照顾到更为一般的情况,并没有采取这种搬移方法,而是采取各个段分别搬移的方法。下面的程序就是搬移RW段。 ??? 130??? InitRam?????????????????????????????????????? ??? 131??????????? ldr?????????? r2,BaSEOfBSS???? ??? 132 ?????????? ldr?????????? r3,BaSEOfZero???? ??? 133??? 0???????????????????????????????????????????????? ??? 134?????????? cmp??????? r2,r3????????????????????? ??? 135?????????? ldrcc??????? r1,#4????????????? ??? 136?????????? strcc?????? r1,#4?????????????? ??? 137?????????? bcc???????? %B0????????????????????? ??? 138?????????? mov??????? r0,#0???????????????????? ???? 139????????? ldr????????? r3,EndOfBSS???????? ???? 140??? 1??????????????????????????????????????????????? ???? 141????????? cmp?????? r2,r3?????????????????????? ???? 142????????? strcc?????? r0,192)">????? 143???????? bcc??????? %B1?????????????????????? ??? 上述的BaSEOfBss和BaSEOfZero仍然是用DCD分配的内存单元。如下: ???????185??????? BaSEOfBss??? DCD??? |Image$$RW$$Base|???? ;RW段起始地址 ?????? 186??????? BaSEOfZero?? DCD??? |Image$$ZI$$Base|??????? ;ZI段起始地址 ?????? 187??????? EndOfBss????? DCD??? |Image$$ZI$$Limit|??????? ;ZI段结束地址 ????? 有了前面RO搬移的讲解,下面RW的搬移工作变得较为简单。在RO搬移完以后,r0已经指向了加载域中RW的起始地址处。 ??? 第131行,将r2指向运行域中RW的起始地址处,其中BaSEOfBSS是在185行定义的。 ??? 第132行,将r3指向RW的结束地址+1处,又因为ZI在RW后面紧挨着存放,所以可以得出这样的结论:RW的结束地址+1就是ZI的起始地址。 ??? RW的搬移如图7所示: ???? 第133行定义了一个局部标号 0。 ???? 第134行比较r2和r3的值。 ???? 第135行,ldr指令后面的cc表示条件执行,如果r2的值小于r3的值,就从r0地址处取出一个字数据,加载到r1中,然后r0自动加4,即r0=r0+4 ???? 第136行,将r1中的数据存储到r2指向的地址处,然后,r2的值自动加4,即r2=r2+4。 ???? 第137行,如果r2的值小于r3,则跳转到133行定义的局部标号0处执行。 ???? 经过前面的讲解,已经实现了RO和RW的搬移工作,下面需要做的就是将ZI初始化为0即可。注意,这里并不是ZI的搬移,因为在加载域中没有ZI,所以不存在搬移这一说法。 ???? 138????????? mov??????? r0,#0???????????????????????? ???? 140??? 1???????????????????????????????????????????????????
?????143???? ???? bcc??????? %B1?????????????????????????? ???? RW搬移结束后,r2指向了RW的结束地址+1处,即ZI的起始地址处。 ???? 第138行,将0赋值给寄存器r0. ?????第139行,将ZI的结束地址+1加载到寄存器r3,即r3指向了ZI的结束地址+1处。程序执行到了现在,加载域中的分布情况如图8所示,可以看出,r2指向了ZI的起始地址,r3指向了ZI的结束地址+1处。 ????? 第140~143行就是将ZI用0填充,即实现了初始化为0。 ???? 到此为止,启动代码做的工作已经接近尾声,下面回顾一下启动代码将程序从加载域到运行域的搬移过程。 ??? ① 找到加载域中各个段的起始地址和结束地址。 ??? 方法:用adr指令找到了RO的起始地址。 ??? ② 找到运行域中各个段的起始地址和结束地址。 ?? 方法: 使用编译器自动生成的各个段的起始地址和结束地址。 ??? ③ 使用循环执行搬移即可。 ? ????? 144???????? ldr???????? r0,=IsrIRQ ????? 146???????? str????????? r1,[r0] ???? 对于第144~146行,就是安装中断向量表。 ?????? 147??????? b???????? Main?? ; ????? 第147行使用b跳转指令跳转到C语言函数Main函数处执行。 ? ? ??? 到这里,我有了一个大概的想法,可不可以在MDK中自带的S3C2440.s中加入这段由加载域搬移到运行域,然后用MDK生成bin文件,利用J-Flash ARM将bin文件下载到nor flash中去,这样子代码就可以搬移到SDRAM中去运行了。不能用axf好像,我看网上说是什么axf携带地址信息,现在还不是很懂。 ?? 我想上述方法可行,关键就是要RO? RW怎么安放,这又好像和分散加载文件scatter有关,再接着看。 RealView MDK中如何获得RO,RW,ZI的地址和长度? ? ? 问题分析: ??? 在RealView MDK里有专门的字符用来表示RO,RW,ZI的起始地址和长度。 ??? 解决办法: ?? 1.在不使用Scatter文件时,默认的为Image$$RW$$Base、Image$$RW$$Limit、Image$$RO$$Base、Image$$RO$$Limit、Image$$ZI$$Base和Image$$ZI$$Limit等6个地址,它的长度这样计算:Length = (Image$$RW$$Limit-Image$$RW$$Base)。 ??? 2. 在使用Scatter文件后,上述的6个默认地址没有了,取而代之的是Image$$段名$$Base 和Image$$段名$$Limit表示的地址,长度计算的方法和上述一样,即Length = (Image$$段名$$Limit-Image$$段名 ??? 3. 关于Scatter文件的使用方法请参考下面的网址:http://www.realview.com.cn/wen-list3.asp?id=330 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |