项目开源地址:https://github.com/Mculover666/uboot-jz2440
在上一篇文章中初步的移植了 uboot 到JZ2440开发板,参考文章:
在支持Nor Flash操作之前,首先要对Nor Flash的读写方法有一定的了解,参考文章:
uboot启动之后正常打印出了CPU信息(比如时钟信息),但是接下来uboot程序提示flash出错,并且死机,如图,在本文中讲述如何定位问题,解决问题,使uboot支持nor flash读写:
定位出错问题所在的方法很简单,定位到红色的日志信息 “Flash:” 在程序中的位置即可,这个内容搜索在VS Code全局搜索中很慢,所以沿着uboot启动过程寻找,很快,在arch/arm/lib/board.c
文件中找到,在函数board_init_r
中,也就是uboot启动的第2阶段。
在代码中可以看到:
此处首先打印出日志信息“Flash:”,然后调用 flash_ini t获取 flash_size,接着对获取到的flash_size进行判断,如果flash_size等于0,则再打印“failed”日志,并且调用hang函数,挂起CPU,即死机:
那么,初步定位,问题就在于 flash_init 函数有问题,进入该函数查看其源码。
flash_init 函数的源码定义在drivers/mtd/cfi_flash.c
文件中,在该函数中,重点代码如图:
可以看到uboot很尽力了,首先调用flash_detect_legacy函数获取Flash的CFI信息,如果获取失败,则调用flash_get_size 函数获取CFI信息,获取之后,将CFI信息中的Flash 大小赋值给size变量,然后返回。
所以,目前问题变为:这两种获取Flash中CFI信息的方法中出现了错误,没有获取到CFI信息。
通过跳转到其定义,可以看到源码是通过宏定义CONFIG_FLASH_CFI_LEGACY配置的,在smdk2440.h文件中搜索一下,可以看到被定义,所以flash_detect_legacy相关方法启用:
在函数的源码中,有一句调试信息通过debug语句输出,可以通过该条语句来查看读取出的三个ID值:
默认情况下,debug函数是被关闭的, 在该文件最开始开启debug的宏定义:
再次编译uboot:
make distclean
make smdk2440_config
make
然后下载u-boot.bin到Nor Flash去,启动,查看串口输出结果:
是不是很棒!
在输出的调试信息中可以看到,Flash的CFI被读出来了,但Flash仍然失败,有待进一步分析,接着根据源代码分析问题原因。
在调试信息中可以看到,读取出的ID信息为c2 2249 0
,查看Nor Flash芯片手册可以看到,0xc2为Manifacture ID,0x2249为Device ID,读出的信息是正确的,但是为什么系统仍然提示Flash出现错误呢?
问题就在于,JEDEC这种古老的标准,它在读取出 ID 信息之后,跑去和Flash库(一个数组)中的信息进行对比匹配,如果匹配到了,就取出预先已经定义好的信息,比如Flash大小之类的信息,如图中红框所示:
jedec_flash_match函数定义在drivers/mtd/jedec_flash.c
文件中,源码如图:
翻译一下这个函数的注释就明白了:
将jedec id 和table相比进行匹配,如果匹配到值,则填充整个flash_info结构体!
所以,读取到的ID值正确,但是系统仍然报错Flash的情况只有一个:预先设置的table中不存在目前的FlashID所匹配的信息。
jedec_table同样定义在该文件中(drivers/mtd/jedec_flash.c
)。
首先是类似于 CONFIG_SYS_FLASH_LEGACY_256Kx8 这样的宏定义,对Flash进行大概的分类,JZ2440开发板板载Nor Flash型号是MX29LV160DBTI-70G,大小是16M-bit
,换算是字节大小就是2MB
。
在jedec_table的最后发给仿照其它信息,添加本款Flash的jedec信息:
该信息为Manifacture ID,在文件include/flash.h
中定义,这里使用MX的:
器件ID在文件drivers/mtd/jedec_flash.c
中宏定义,如图,添加本款Flash的:
这个按喜好写就行。
使用16位的解锁地址宏定义 MTD_UADDR_0x0555_0x02AA 即可。
容量大小也在drivers/mtd/jedec_flash.c
文件中设置,这里选择SIZE_2MiB:
使用默认的CFI_CMDSET_AMD_LEGACY即可。
使用ERASEINFO()宏定义填写上面擦除区域种类的详细信息,该宏定义的格式如下:
ERASEINFO(size,blocks) //表示sIze大小(单位是字节)的扇区有blocks个
综合以上分析,最后填充的本款Flash信息为:
/* JZ2440开发板板载NoR FLASH */
#ifdef CONFIG_SYS_FLASH_LEGACY_1024Kx16
{
.mfr_id = (u8)MX_MANUFACT,
.dev_id = MX29LV160DBTI,
.name = "MX MX29LV160DBTI",
.uaddr = {
[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = CFI_CMDSET_AMD_LEGACY,
.NumEraseRegions = 4,
.regions = {
ERASEINFO(16*1024, 1),
ERASEINFO(8*1024, 2),
ERASEINFO(32*1024, 1),
ERASEINFO(64*1024, 31),
}
},
#endif
添加信息完成后,在单板配置文件include/configs/smdk2440.h
中开启这款Flash的宏定义:
编译,下载,在串口终端查看输出:
可以看到flash检测不会卡死,uboot正常启动到命令行界面,但是仍然有一个报错信息,根据报错信息在文件中搜索,找到该行日志的打印位置,可以看到,当扇区数量大于宏定义CONFIG_SYS_MAX_FLASH_SECT时,系统就会打印出这行日志信息:
找到该宏定义在单板配置文件中,该款Flash实际扇区是35个,修改该宏定义:
编译,下载,再次查看串口输出:
可以看到,Flash检测没有任何问题,uboot正常启动到命令行界面,并且命令可以正常使用,调试完毕,不用输出调试信息,所以将drivers/mtd/cfi_flash.c
文件中开头添加的宏定义关掉:
再次编译输出,可以看到Flash的检测调试信息不会打印。
在之前的打印信息中,uboot命令行可以正常使用,但是打印头信息还是"smdk2410",这里可以在单板配置文件include/configs/smdk2440.h
中修改宏定义,如图,改为我们需要显示的信息:
修改之后再次编译,下载,查看串口输出,结果符合预期:
补充,可以使用uboot命令flinfo
查看当前flash存储器信息,与上面的知识进行对比理解: