之前因项目需要做了一下2148x的bootloader,于是在参考几份资料后对bootloader有所了解。这些资料包括:ADSP-214xx_hwr_rev1.1、EE345、50_ldr_mn_rev_2.5和cces_1-0-2_loader_man_rev.1-2,其中50_ldr_mn_rev_2.5和cces_1-0-2_loader_man_rev.1-2是两份基本相似的文档。至于这些文档,笔者看过之后做了一些笔记,均提供给大家参考。以下所说的bootloader若有不正确地方,请及时纠正更改,此外,由于笔者只做过spi flash的启动,所以文档中也只会对spi flash的启动作详细描述。
首先说说程序是如何boot起来的,主要有两部分:首先启动后,根据选定为spi flash模式,先发送读指令从flash的0x000000地址中将一个256 instruction words(48-bit)的小程序load进芯片,这个小程序就是boot kernel。这个小程序在芯片中跑起来,然后根据一些必要信息引导,将真正应用程序load入芯片,但不包括IVT(interrupt vector table)。注意,boot kernel本身就是在sharc所对应的IVT的位置中跑起来的,也即0x0008c000~0x0008c0ff这段地址中。load完application后,boot kernel会用SPI的DMA将自身改写为application对应的IVT(听起来似乎很神奇),至此,boot kernel的工作完成,加载过程结束,开始跑application。
接下来,是时候说一下在上面反复提及的boot kernel。这是一个用asm写的小程序,由官方给出,用户基本上不需要进行过多修改,只是在做secondbootloader的时候需要进行一定修改,但例程都在EE345对应的参考程序中可查看。boot kernel里面的label主要分为3类:首先,第一部份也是最为重要,或者说用户可修改用到的是“user_init:” 这是boot kernel为用户自定义代码所预留的,我们所做的不默认地从spi flash的0x00000地址而从其余的诸如0x010000、0x020000等地址将程序读进来的bootloader功能就是在这里面完成的,所以,一般用户只需要修改删减该label下对应的汇编代码,其余地方的代码不建议进行修改。更具体地说,在user_init中主要是配置一个SPI DMA发送spi flah的读指令,并且在读指令中还指定了app在flash中特定的存放地址。注意,此处并不关心读进来的数据是什么,甚至并不保存,只是为了发送读指令以及flash的偏移地址,为接下来的load入flash中偏移后APP并进行保存做准备。还需注意的是,因为boot kernel最大只能支持256 instruction words,所以修改user_init之后仍需保证boot kernel的大小不超过256 instruction words。如果用户用的不是笔者给出的参考程序,而是自己新建工程的话,对于asm编译器的编译选项必须选择为“Generate Normal Word code”,选择为"Generate Short Word code"会产生不可思议的错误,具体用户可自行尝试。
接下来的两类label不建议,或者说禁止新手用户进行修改或者删减,所以对于这两类label笔者只说明其功能。接上述第一类label,第二类label中主要包括了诸如:ZERO_LDATA、ZERO_L48、INIT_L16、INIT_L32、INIT_L48、INIT_L64、ZERO_EXT8、ZERO_EXT16、INIT_EXT8、INIT_EXT16等。这些label的存在主要是为了真正地实现load用户的app,并放到用户app指定的相对应的地方。这是由于app的每个section都包括了header,header中包括了:data tag(也即对应了上面所说的label)、data address、data count,boot kernel根据section header的信息循环运行以上的label,完成app程序的加载。具体用户可详见boot kernel源码,这里不做累述。
运行完第二类label,boot kernel也行将就木来到最后的label也即第三类label:final_init,部分笔者认为较为复杂,用户只需要这是boot kernel的最后部分,用来load用户app对应的IVT到0x0008c000~0x0008c0ff以把boot kernel干掉就好了。具体实现过程不累述。
最后,说说我上传的简单检测代码,代码是我 根据EE345修改的,用户可无需修改就可使用。在代码中,我是通过点亮不同的LED灯来判别跑的是哪个程序。工程分两个,一个是second_bootloader一个是application工程,并且在newproject这个application工程中使用了*pSYSCTL = SRST;进行了软复位,用户如不需要可将其去除。需注意的是load_application_serial()这个函数,或者说label,也即我们在上面提及的boot kernel,笔者是将其放在 block0中去了:seg_ldr { TYPE(PM RAM) START(0x0008c100) END(0x0008c1ff) WIDTH(48) }。其实放在哪里都没所谓,关键是接下来的APP中不要使用到这段空间也即用户程序必须空出这么一段空间给seg_ldr,这个在EE345中memory usage restriction有详细提及,简单说来就是因为这时的这个load_application_serial()函数不再是ADI给出的默认的boot kernel而是被修改过的,所以不再存放在IVT那段地址,如果app中的地址跟其重复的话会在我们上面所说的第二类label运行阶段将这个函数给覆盖,这样的话,load了一半app,或者说app不知道load到哪里就因为load app的程序被覆盖产生灾难性错误,接下来的app无法load更别说load完后run app了。
PS:需提出注意的是,正如像EE345里面提到的,在用户所需的偏移量app_offset_serial的基础上,在“user_init:”中其实还是加上了一个0x600长度为1536bytes的偏移量,用来跳过app生成ldr文件时所附带的默认boot kernel(256 instruction words=1536bytes)。所以用户在生成app的ldr时无需再编译选项中选择“No kernel”选项,因为其所embedded的boot kernel无效。
详细请见:http://www.openadsp.cn/topic.asp?id=2563&boardid=5&tb=1
|