2024年3月9日发(作者:宜弘文)
AT91RM9200启动源代码crt0.s中的错误分析
本人的AT91RM9200采用从norflash启动方式,产品采用的norflash型号为JS28F128J3D75,后来更换为JS28F128J3F75。当采用JS28F128J3D75时代码正常启动,当采用JS28F128J3F75时代码无法正常启动,程序卡在了clearbss函数中,因此对启动文件BOOT源代码进行了分析。
系统上电检测到BMS为低电平时,系统将外部norflash地址映射为0x0,并且CPU从0x0地址处读取命令执行。此地址norflash存储的是BOOT程序。BOOT程序所包含的源代码包括:entry.S、crt0.S、initboot.c、main.c等。
程序在entry.S中初始化时钟,调用AT91F_LowLevelInit初始化函数,初始化各种运行模式下的堆栈范围,然后跳转至_main函数中,_main函数在crt0.S文件中,进行了数据拷贝和bss数据初始化工作,然后跳转至main函数,进行端口打印和uboot解压缩。流程如下图所示:
上电BMS为0,norflash地址为0x0B InitReset
初始化时钟initboot.c:初始化sdram、flash、串口等 AT91F_LowLevelInit初始化堆栈b _main程序中定义的有初始值的全局变量的初始值存在sdata段,将本段的数据拷贝至变量区。程序中定义的未初始化的全局变量,在变量区初始化为0拷贝变量初始值crt0.S未初始化变量置0b boot
Gcc在编译时按照链接文件描述语言将源代码中的程序和只读变量放置在text段,将已初始化的全局变量(包括初始化为0)按照地址排列至data段,将未初始化的全局变量按照地址排列值bss段。
:
MEMORY {
ram : ORIGIN = 0x20000000, LENGTH = 0xf000
rom : ORIGIN = 0x00000000, LENGTH = 0xf000
main.c解压缩UBOOT
}
SECTIONS {
.text : {
_stext = . ;
*(.text)
*(.rodata)
. = ALIGN(4);
_etext = . ;
} > rom
.data : {
_sdata = . ;
*(.data)
*(.glue_7*)
. = ALIGN(4);
_edata = . ;
} > ram
.bss : {
_sbss = . ;
*(.bss)
. = ALIGN(4);
_ebss = . ;
} > ram
}
由文件可以看出,text段在rom区,起始地址为0x0,data段在ram区,起始地址为0x20000000,bss段紧接着data段存储。链接文件定义了c程序运行环境参数,当c程序运行时,全局变量便会去0x20000000开始的ram区相应的地址去查找。在c程序中引用的全局变量按照地址的形式使用,地址范围在ram地址规定的范围里面。
当设备上电后,程序从flash执行,全局变量从ram中读取,由于ram是易失性的,上电时里面的内容是乱的,因此在全局变量使用之前需要首先对全局变量区域进行初始化,这些初始化值被烧写在了flash中。因此烧写在flash中的boot文件结构为:
_stextCode_etextData
因此在上电时程序执行copydata函数就是从flash中将全局变量初始值拷贝至data段,并且将ram中bss段数据清0,执行完后,数据结构如下图所示:
_stextCode_etext_sdataData_edata_sbssData_ebss0x00x20000000
此时,运行带有全局变量c程序的环境已经建立起来。
由上面分析可知,程序运行时copydata函数是将flash地址_etext开始的一段内容拷贝到0x20000000开始的地址内,大小为(_edata-_sdata)。Clearbss函数是将_sbss开始的一段内容清0。Crt0.s内容如下:
@ r0 -> start of flash
@ r1 -> where to load data
@ r2 -> start of program
.text
.align
.global main,_main
main:
_main:
# copy .data section
ldr r3, =_etext
ldr r4, =_sdata
ldr r5, =_edata
subs r5, r5, r4
bl copydata
# clear .bss section
ldr r4, =_sbss
ldr r5, =_ebss
subs r5, r5, r4
mov r0, #0
bl clearbss
# and jump to the kernel
b boot
copydata:
subs r5, r5, #4
ldr r6, [r3], #4
str r6, [r4], #4
bne copydata
mov pc, lr
clearbss:
subs r5, r5, #4
str r0, [r3], #4
bne clearbss
mov pc, lr
由代码可以看出,clearbss函数是将flash中烧写的全局变量初始值区后开始的bss段大小空间清0。而在实际使用中,程序会去找ram空间中的bss段,而非flash段,因此上面的程序存在错误,应该将“str r0, [r3], #4”改为“str r0, [r4], #4”,通过实验验证了这一结论。
在main.c文件文件中声明一个未初始化的全局变量,并将这一全局变量地址和内容打印出来,发现此地址在ram空间bss段,且值不为0,也就证明了原先的clearbss函数没有起到应有的作用。
当把“str r0, [r3], #4”改为“str r0, [r4], #4”后,重新试验,此变量值为0,验证了修改的正确性。
另外,如果声明的全局变量初始化为0,则打印出来的结果显示,此变量在data段,并不在bss段,因此不论clearbss是否正确,打印出来值都是0。
通过分析IAR编译器编译完成的stm32f103的启动代码发现烧写在flash中的全局变量初始值是经过算法优化的,所以在flash中data区大小是要小于ram中data区大小的。
修改crt0.s后,焊接JS28F128J3F75的板卡正常启动,问题得以解决。但是为什么未修改前JS28F128J3D75可以启动,JS28F128J3F75无法启动,却不得而知。通过分析两颗芯片的对比文件并未发现有太多的不同,仅仅增加了密码保护命令和对错误命令处理机制上不同。通过前面的试验发现焊接JS28F128J3D75时,clearbss也并未真正把相应的flash空间清0。怀疑是clearbss非正常写入flash时两颗芯片的处理方式不同吧,由于问题已经解决,也就不再去花太多力气去分析了。
2024年3月9日发(作者:宜弘文)
AT91RM9200启动源代码crt0.s中的错误分析
本人的AT91RM9200采用从norflash启动方式,产品采用的norflash型号为JS28F128J3D75,后来更换为JS28F128J3F75。当采用JS28F128J3D75时代码正常启动,当采用JS28F128J3F75时代码无法正常启动,程序卡在了clearbss函数中,因此对启动文件BOOT源代码进行了分析。
系统上电检测到BMS为低电平时,系统将外部norflash地址映射为0x0,并且CPU从0x0地址处读取命令执行。此地址norflash存储的是BOOT程序。BOOT程序所包含的源代码包括:entry.S、crt0.S、initboot.c、main.c等。
程序在entry.S中初始化时钟,调用AT91F_LowLevelInit初始化函数,初始化各种运行模式下的堆栈范围,然后跳转至_main函数中,_main函数在crt0.S文件中,进行了数据拷贝和bss数据初始化工作,然后跳转至main函数,进行端口打印和uboot解压缩。流程如下图所示:
上电BMS为0,norflash地址为0x0B InitReset
初始化时钟initboot.c:初始化sdram、flash、串口等 AT91F_LowLevelInit初始化堆栈b _main程序中定义的有初始值的全局变量的初始值存在sdata段,将本段的数据拷贝至变量区。程序中定义的未初始化的全局变量,在变量区初始化为0拷贝变量初始值crt0.S未初始化变量置0b boot
Gcc在编译时按照链接文件描述语言将源代码中的程序和只读变量放置在text段,将已初始化的全局变量(包括初始化为0)按照地址排列至data段,将未初始化的全局变量按照地址排列值bss段。
:
MEMORY {
ram : ORIGIN = 0x20000000, LENGTH = 0xf000
rom : ORIGIN = 0x00000000, LENGTH = 0xf000
main.c解压缩UBOOT
}
SECTIONS {
.text : {
_stext = . ;
*(.text)
*(.rodata)
. = ALIGN(4);
_etext = . ;
} > rom
.data : {
_sdata = . ;
*(.data)
*(.glue_7*)
. = ALIGN(4);
_edata = . ;
} > ram
.bss : {
_sbss = . ;
*(.bss)
. = ALIGN(4);
_ebss = . ;
} > ram
}
由文件可以看出,text段在rom区,起始地址为0x0,data段在ram区,起始地址为0x20000000,bss段紧接着data段存储。链接文件定义了c程序运行环境参数,当c程序运行时,全局变量便会去0x20000000开始的ram区相应的地址去查找。在c程序中引用的全局变量按照地址的形式使用,地址范围在ram地址规定的范围里面。
当设备上电后,程序从flash执行,全局变量从ram中读取,由于ram是易失性的,上电时里面的内容是乱的,因此在全局变量使用之前需要首先对全局变量区域进行初始化,这些初始化值被烧写在了flash中。因此烧写在flash中的boot文件结构为:
_stextCode_etextData
因此在上电时程序执行copydata函数就是从flash中将全局变量初始值拷贝至data段,并且将ram中bss段数据清0,执行完后,数据结构如下图所示:
_stextCode_etext_sdataData_edata_sbssData_ebss0x00x20000000
此时,运行带有全局变量c程序的环境已经建立起来。
由上面分析可知,程序运行时copydata函数是将flash地址_etext开始的一段内容拷贝到0x20000000开始的地址内,大小为(_edata-_sdata)。Clearbss函数是将_sbss开始的一段内容清0。Crt0.s内容如下:
@ r0 -> start of flash
@ r1 -> where to load data
@ r2 -> start of program
.text
.align
.global main,_main
main:
_main:
# copy .data section
ldr r3, =_etext
ldr r4, =_sdata
ldr r5, =_edata
subs r5, r5, r4
bl copydata
# clear .bss section
ldr r4, =_sbss
ldr r5, =_ebss
subs r5, r5, r4
mov r0, #0
bl clearbss
# and jump to the kernel
b boot
copydata:
subs r5, r5, #4
ldr r6, [r3], #4
str r6, [r4], #4
bne copydata
mov pc, lr
clearbss:
subs r5, r5, #4
str r0, [r3], #4
bne clearbss
mov pc, lr
由代码可以看出,clearbss函数是将flash中烧写的全局变量初始值区后开始的bss段大小空间清0。而在实际使用中,程序会去找ram空间中的bss段,而非flash段,因此上面的程序存在错误,应该将“str r0, [r3], #4”改为“str r0, [r4], #4”,通过实验验证了这一结论。
在main.c文件文件中声明一个未初始化的全局变量,并将这一全局变量地址和内容打印出来,发现此地址在ram空间bss段,且值不为0,也就证明了原先的clearbss函数没有起到应有的作用。
当把“str r0, [r3], #4”改为“str r0, [r4], #4”后,重新试验,此变量值为0,验证了修改的正确性。
另外,如果声明的全局变量初始化为0,则打印出来的结果显示,此变量在data段,并不在bss段,因此不论clearbss是否正确,打印出来值都是0。
通过分析IAR编译器编译完成的stm32f103的启动代码发现烧写在flash中的全局变量初始值是经过算法优化的,所以在flash中data区大小是要小于ram中data区大小的。
修改crt0.s后,焊接JS28F128J3F75的板卡正常启动,问题得以解决。但是为什么未修改前JS28F128J3D75可以启动,JS28F128J3F75无法启动,却不得而知。通过分析两颗芯片的对比文件并未发现有太多的不同,仅仅增加了密码保护命令和对错误命令处理机制上不同。通过前面的试验发现焊接JS28F128J3D75时,clearbss也并未真正把相应的flash空间清0。怀疑是clearbss非正常写入flash时两颗芯片的处理方式不同吧,由于问题已经解决,也就不再去花太多力气去分析了。