支持了nand flash启动并不是说支持了以后对nand flash的读写操作,支持nand flash启动只需要在重定位时(把代码从nand flash拷贝到SDRAM的函数copy_code_to_sdram)x写出一个nand flash的读函数nand_read_ll(),吧代码从nand flash复制到sdram里面去就可以了,
1、分析
(1)烧写代码,重启开发板,查看输出信息
输出信息表示flash失败后重启开发板
(2)在u-boot中搜索代码“Flash:”,在board.c中存在
(3)board.c分析
打印“Flash:”的语句
初始化 flash,如果flash的大小大于0就打印flash的大小,否则打印failed,调用hang()函数,flash_init是对nor flash的初始化函数
hang()函数是打印出错误信息,然后死循环,让系统在此卡死。
(4)修改
把hang函数注释掉,也就是把死循环去掉,程序就可以继续往下跑。加上puts("0KB\n\r")这一段是为了避免从nand flash启动时,会在这里卡死,因为从nand flash启动时,那个nor flash是没办法访问的,
(5)归根结底要分析flash_init()函数
怎样才能让flash_init()函数里面识别出我们的nor flash
也就是下面所说的数组jedec_table[]里面的项
2、看flash_init()函数
(1)搜索此函数,在driverst/mtd/cfi_flash.c
(2)第1条语句是指用旧的方法检测nor flash,如果不成功再用下面那种方法检测nor flash
(3)第1条语句flash_detect_legacy()分析
先看宏cfi_flash_bank_addr,在linux下搜索
在100行中里有注释
宏CONFIG_SYS_FLASH_BANK_LIST为0x00000000,也就是最终返回0地址
也就是说flash_detect_legacy()左边参数是0,经过分析用旧的方法读不出来。
(2)新的方法flash_get_size(),实现了nor flash的访问
打印调试信息
debug函数的宏定义如下
这里debug_cond函数的实现,如果cond存在就打印信息,也就是说根据宏_DEBUG为1时,才能打印
所以定义宏
(3)新的方法flash_get_size(),实现了nor flash的访问
只要确定基地址、块数
(4)编译、烧写
先拷贝修改过的board.c和cfi_flash.c
要烧写到nor flash里面去,因为要从nor flash启动。
输入flinfo命令查看( flinfo 打印Flash存储器的信息,并列出所有Sector。flinfo N 单独打Flash存储器N Block的信息)
下面的RO是uboot在软件上指定相应的块是只读的,想烧写它的话需要执行protect off all ,这是软件上的一种限制
3、看打印信息分析
(1)下面是nor flash发出的各种命令(u-boot下对nor flash的操作)。识别出ID
(2)搜索ID
打印出ID,用jedec_flash_match函数看是否匹配,看打印信息是读出来了,但跟数组比较不成功
jedec_flash_match函数会把ID值跟某个数组jedec_table[]比较,如果数组没有含有他表示找不到。我们的是1M×16位的,如果找到就知道nor flash的参数
在数组后面添加一项
第一个是厂家ID,第二个是设备ID,第三个是名字,第4个是解锁地址,nor flash可以像内存一样读,但是要写它的话就必须先解锁,解完锁后发出命令,下面的555和2AA是两个解锁地址,解锁地址是nor flash看到的地址,而后面的555是发命令地址,0表示8位,1表示16位;第5个参数是设备大小为2M,第6个参数是命令集,第7个参数是擦除区域的个数,第8个参数是大小为16K(16*1024字节)的块的个数是1......
(3)看jz2440板中nor flash芯片的手册,看他的ID
我的nor flash芯片型号是mx29lv160db,搜索到相关手册,下图是读取厂家ID和设备ID 的命令操作,经过3个周期写入指令,再经过一个周期读取地址00,得厂家ID值c2,我们的nor flash是16位的,看原理图可知,因而用word.对于设备ID,可由下面知为2249
(4)关于解锁地址
由于CPU是8位的,而nor flash是16位的,因而CPU的A0不接,CPU的A1接到nor flash里面的A0,我们从芯片手册上看到的地址555和2AA是nor flash自身感受到的,我们CPU发出的地址是555左移1位。
(5)关于擦除区域
也就是nor flash的擦除块是多少。只有一种格式是为1,有两种格式是2个,如下面块的结构有4种,因而有4种擦除区域。
(6)块的结构
总共有4个,这里16KB的有一个,8KB的有2个,32KB的有1个,64KB的有31个
4、烧写实验
(1)用旧u-boot引导新u-boot下载
(2)我们u-boot本身有512K,转换成16进制是80000.因而擦除80000后的数据,擦除80000到90000.从内存30000000拷贝10000到nor flash的80000那里,也就是64K。
用md.b命令查看内存30000000的内容
用md.b命令查看nor flash为80000的内容
往地址30000000这个地址写值12345678,然后重新擦除nor flash,然后重新拷贝,cp.b命令会分辨80000这个地址,发现是nor flash的地址后,会调用nor flash的写函数发出各种命令,然后再来烧写数据。用md.b 80000命令查看地址为80000的数据。出现78是因为md.b中b是字节的意思,也就是说只写一个字节。
用cmp.b命令比较内存地址30000000和nor flash地址80000的10000字节数据是否全是一样的
从上面可以看出有一个地方不对,是否源里面的数据被改变?
修改内存地址,也就是复制不同的内存中的内容,从30000000改为32000000.复制后内容完全一样。
为什么之前的30000000地址烧写进去有问题,现在的32000000地址就没问题,是不是之前的设置有问题。看看内存有没有被用到。内存之前设置了栈(在0x30000f80这个地方),但是在启动函数board_init_f里面会把gd_t结构体复制到SDRAM 的高地址上去,以后用的都是高地址那一块内存。难道栈没有被重新设置设置,从0x30000f80转向指到高地址的地方。
栈没有被重新设置,没有从0x30000f80转向指到高地址的地方。也就是栈一直在0x30000f80这个地方,每调用一个C函数,栈都会有变化,所以30000000后的地方由于函数的执行导致里面的数据发生变化。
看一下C语言调用汇编,是先在汇编里先用global关键字定义,表示是全局的变量。在C语言中用extern定义关键字,表示是外部文件变量。
因而重新设置栈
在C函数中定义、保存变量board.c
在汇编中start.S
需要先跳到SDRAM里面执行,然后才重新设置栈,
这条指令才会在SDRAM里面设置那个变量。如果不先跳到SDRAM里面去的话,有可能变量base_sp现在在nor flash上面。去操作它时无法读写,因为是用相对跳转的指令取地址的。
用超级终端(用串口下载)是迫不得已的办法