前兩天在俺們的EVB上加了點東西,增加了一下的BL語句進行初始化:
BL C2g_GsmStartZac
LDR pc, =INT_Initialize ;goto main in RAM
接着就出了一個奇怪的問題,使用RVDS,將PC置於代碼燒錄的其實地址0x40004,然後F5開跑,可以正常開跑。但是如果使用板子自帶的boot進行啓動,則跑不起來。
使用RVDS從boot開始跟蹤,發現原來boot跳到主程序的過程中,跳入的地址爲0x40040004,之所以這麼做主要是爲了和操作flash的代碼進行統一,因爲0-32K的Flash空間在系統運行的過程中被映射爲內部IRAM,如果要操作這段Flash,則需要從0x4000000開始,通過地址繞轉來巧妙的繞過這個問題。系統用了多年沒出問題,其實是沒在這個地方調用過IRAM裏的函數。
本來,如果BL跳轉的地址C2g_GsmStartZac,如果也是在Flash中,則不會有任何問題,反正地址都是繞轉。另外,如果跳轉到地址如果超出了BL的能力範圍32M,編譯器(鏈接器)本來也會想辦法解決這個問題的,即將C2g_GsmStartZac的地址保存在Flash中的某個位置,然後通過LDR把地址加載到寄存器,再通過寄存器跳轉。
出現前面說的問題的關鍵在於,這個地址正好位於0-32k的空間內,而且由於scatter loader文件中的鏈接地址又是0x40000開始的,導致編譯器認爲正好在BL跳轉的32M範圍內,直接編譯成了通過偏移條狀,即BL跳轉是在當前PC的基礎上加上偏移量來跳轉的,因此就有了問題。
通過反彙編知道,使用0x40000進行鏈接,生成的代碼如下:
0x00040060: fbff0269 i... BLX C2g_GsmStartZac ; 0xa0e
根據文檔《ARM Instruction》對BLX的介紹如下:
其中的0-23位爲有符號的偏移,指令fbff0269中對應的爲0-22位爲0x7f0269, 第23爲1,表示負數,即負的0xFD97, 在左移兩位爲負的0x3F65c,加上H<<1, H爲1, 運算結果爲負的0x3F65A. 用PC值0x40060-3f65a=0xA06.和0xa0e差8,正好符合BLX指令下面的註釋。但是當鏈接地址爲0x40000開始,boot跳轉卻是到0x4000000時,此處的PC變成了0x4000060了,運算結果變成了0x4000060-3f65a=3FC0A06,訪問的是繞轉的Flash的內容,當然就有問題了。
實際上如果scatter loader寫爲0x4000000則,鏈接器處理的結果是這樣的,使用了臨時變量。
0x40000060: eb02bc1c .... BL $Ven$AT$L$$C2g_GsmStartZac ; 0x400af0d8
...........................
$Ven$AT$L$$C2g_GsmStartZac
$a
0x400af0d8: e59fc000 .... LDR r12,0x400af0e0
0x400af0dc: e12fff1c ../. BX r12
$d
$f
0x400af0e0: 00000a0f .... DCD 2575 ;即0xA0F, thumb
但是由於系統已經做好了,改變scatter loader的地址還要牽扯到MMU的配置,Flash的寫入cache等諸多問題,最後的解決方法就是使用不會產生歧義的調用方法,即直接使用pc跳轉
mov lr, pc
LDR pc, =C2g_GsmStartZac
LDR pc, =INT_Initialize ;goto main in RAM
這樣在什麼位置跳轉都沒有問題了,不過先得把pc保存到lr中(上面的第一句),不然調用後就回不來了。