之前因項(xiàng)目需要做了一下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若有不正確地方,請及時(shí)糾正更改,此外,由于筆者只做過spi flash的啟動(dòng),所以文檔中也只會對spi flash的啟動(dòng)作詳細(xì)描述。
首先說說程序是如何boot起來的,主要有兩部分:首先啟動(dòng)后,根據(jù)選定為spi flash模式,先發(fā)送讀指令從flash的0x000000地址中將一個(gè)256 instruction words(48-bit)的小程序load進(jìn)芯片,這個(gè)小程序就是boot kernel。這個(gè)小程序在芯片中跑起來,然后根據(jù)一些必要信息引導(dǎo),將真正應(yīng)用程序load入芯片,但不包括IVT(interrupt vector table)。注意,boot kernel本身就是在sharc所對應(yīng)的IVT的位置中跑起來的,也即0x0008c000~0x0008c0ff這段地址中。load完application后,boot kernel會用SPI的DMA將自身改寫為application對應(yīng)的IVT(聽起來似乎很神奇),至此,boot kernel的工作完成,加載過程結(jié)束,開始跑application。
接下來,是時(shí)候說一下在上面反復(fù)提及的boot kernel。這是一個(gè)用asm寫的小程序,由官方給出,用戶基本上不需要進(jìn)行過多修改,只是在做secondbootloader的時(shí)候需要進(jìn)行一定修改,但例程都在EE345對應(yīng)的參考程序中可查看。boot kernel里面的label主要分為3類:首先,第一部份也是最為重要,或者說用戶可修改用到的是“user_init:” 這是boot kernel為用戶自定義代碼所預(yù)留的,我們所做的不默認(rèn)地從spi flash的0x00000地址而從其余的諸如0x010000、0x020000等地址將程序讀進(jìn)來的bootloader功能就是在這里面完成的,所以,一般用戶只需要修改刪減該label下對應(yīng)的匯編代碼,其余地方的代碼不建議進(jìn)行修改。更具體地說,在user_init中主要是配置一個(gè)SPI DMA發(fā)送spi flah的讀指令,并且在讀指令中還指定了app在flash中特定的存放地址。注意,此處并不關(guān)心讀進(jìn)來的數(shù)據(jù)是什么,甚至并不保存,只是為了發(fā)送讀指令以及flash的偏移地址,為接下來的load入flash中偏移后APP并進(jìn)行保存做準(zhǔn)備。還需注意的是,因?yàn)閎oot kernel最大只能支持256 instruction words,所以修改user_init之后仍需保證boot kernel的大小不超過256 instruction words。如果用戶用的不是筆者給出的參考程序,而是自己新建工程的話,對于asm編譯器的編譯選項(xiàng)必須選擇為“Generate Normal Word code”,選擇為"Generate Short Word code"會產(chǎn)生不可思議的錯(cuò)誤,具體用戶可自行嘗試。
接下來的兩類label不建議,或者說禁止新手用戶進(jìn)行修改或者刪減,所以對于這兩類label筆者只說明其功能。接上述第一類label,第二類label中主要包括了諸如:ZERO_LDATA、ZERO_L48、INIT_L16、INIT_L32、INIT_L48、INIT_L64、ZERO_EXT8、ZERO_EXT16、INIT_EXT8、INIT_EXT16等。這些label的存在主要是為了真正地實(shí)現(xiàn)load用戶的app,并放到用戶app指定的相對應(yīng)的地方。這是由于app的每個(gè)section都包括了header,header中包括了:data tag(也即對應(yīng)了上面所說的label)、data address、data count,boot kernel根據(jù)section header的信息循環(huán)運(yùn)行以上的label,完成app程序的加載。具體用戶可詳見boot kernel源碼,這里不做累述。
運(yùn)行完第二類label,boot kernel也行將就木來到最后的label也即第三類label:final_init,部分筆者認(rèn)為較為復(fù)雜,用戶只需要這是boot kernel的最后部分,用來load用戶app對應(yīng)的IVT到0x0008c000~0x0008c0ff以把boot kernel干掉就好了。具體實(shí)現(xiàn)過程不累述。
最后,說說我上傳的簡單檢測代碼,代碼是我 根據(jù)EE345修改的,用戶可無需修改就可使用。在代碼中,我是通過點(diǎn)亮不同的LED燈來判別跑的是哪個(gè)程序。工程分兩個(gè),一個(gè)是second_bootloader一個(gè)是application工程,并且在newproject這個(gè)application工程中使用了*pSYSCTL = SRST;進(jìn)行了軟復(fù)位,用戶如不需要可將其去除。需注意的是load_application_serial()這個(gè)函數(shù),或者說label,也即我們在上面提及的boot kernel,筆者是將其放在 block0中去了:seg_ldr { TYPE(PM RAM) START(0x0008c100) END(0x0008c1ff) WIDTH(48) }。其實(shí)放在哪里都沒所謂,關(guān)鍵是接下來的APP中不要使用到這段空間也即用戶程序必須空出這么一段空間給seg_ldr,這個(gè)在EE345中memory usage restriction有詳細(xì)提及,簡單說來就是因?yàn)檫@時(shí)的這個(gè)load_application_serial()函數(shù)不再是ADI給出的默認(rèn)的boot kernel而是被修改過的,所以不再存放在IVT那段地址,如果app中的地址跟其重復(fù)的話會在我們上面所說的第二類label運(yùn)行階段將這個(gè)函數(shù)給覆蓋,這樣的話,load了一半app,或者說app不知道load到哪里就因?yàn)閘oad app的程序被覆蓋產(chǎn)生災(zāi)難性錯(cuò)誤,接下來的app無法load更別說load完后run app了。
PS:需提出注意的是,正如像EE345里面提到的,在用戶所需的偏移量app_offset_serial的基礎(chǔ)上,在“user_init:”中其實(shí)還是加上了一個(gè)0x600長度為1536bytes的偏移量,用來跳過app生成ldr文件時(shí)所附帶的默認(rèn)boot kernel(256 instruction words=1536bytes)。所以用戶在生成app的ldr時(shí)無需再編譯選項(xiàng)中選擇“No kernel”選項(xiàng),因?yàn)槠渌鵨mbedded的boot kernel無效。
詳細(xì)請見:http://www.openadsp.cn/topic.asp?id=2563&boardid=5&tb=1
|