前两天在俺们的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中(上面的第一句),不然调用后就回不来了。
阅读(3477) | 评论(0) | 转发(0) |