之前因項目需要做了一下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起來的,主要有兩部分:首先啟動后,根據(jù)選定為spi flash模式,先發(fā)送讀指令從flash的0x000000地址中將一個256 instruction words(48-bit)的小程序load進芯片,這個小程序就是boot kernel。這個小程序在芯片中跑起來,然后根據(jù)一些必要信息引導,將真正應用程序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發(fā)送spi flah的讀指令,并且在讀指令中還指定了app在flash中特定的存放地址。注意,此處并不關心讀進來的數(shù)據(jù)是什么,甚至并不保存,只是為了發(fā)送讀指令以及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"會產(chǎn)生不可思議的錯誤,具體用戶可自行嘗試。
接下來的兩類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的存在主要是為了真正地實現(xiàn)load用戶的app,并放到用戶app指定的相對應的地方。這是由于app的每個section都包括了header,header中包括了:data tag(也即對應了上面所說的label)、data address、data count,boot kernel根據(jù)section header的信息循環(huán)運行以上的label,完成app程序的加載。具體用戶可詳見boot kernel源碼,這里不做累述。
運行完第二類label,boot kernel也行將就木來到最后的label也即第三類label:final_init,部分筆者認為較為復雜,用戶只需要這是boot kernel的最后部分,用來load用戶app對應的IVT到0x0008c000~0x0008c0ff以把boot kernel干掉就好了。具體實現(xiàn)過程不累述。
最后,說說我上傳的簡單檢測代碼,代碼是我 根據(jù)EE345修改的,用戶可無需修改就可使用。在代碼中,我是通過點亮不同的LED燈來判別跑的是哪個程序。工程分兩個,一個是second_bootloader一個是application工程,并且在newproject這個application工程中使用了*pSYSCTL = SRST;進行了軟復位,用戶如不需要可將其去除。需注意的是load_application_serial()這個函數(shù),或者說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()函數(shù)不再是ADI給出的默認的boot kernel而是被修改過的,所以不再存放在IVT那段地址,如果app中的地址跟其重復的話會在我們上面所說的第二類label運行階段將這個函數(shù)給覆蓋,這樣的話,load了一半app,或者說app不知道load到哪里就因為load app的程序被覆蓋產(chǎn)生災難性錯誤,接下來的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
|