uboot移植之修改支持NandFlash识别篇6(超详细)

uboot移植之前期准备篇1

uboot移植之Makefile分析概述篇2

boot移植之init_sequence_f函数数组分析(番外篇)

uboot移植之源码流程分析篇3(超详细!)

uboot移植之修改支持SDRAM篇4

uboot移植之修改支持NorFlash篇5


目录

1、预备知识

2、开发文件对象2410 -> 2440

3、修改分析代码

4、遗留问题

5、遗留问题“NAND write to offset 0 failed -5”解决方法


1、预备知识

参考《K9F2G08U0C》手册章节“Pin Description”,可知道Nand Flash各引脚的使用说明如下图:

在操作Nand的时候,只需要往特定的寄存器中写值/读值,就可以实现发地址,发数据,读数据的功能了,中间一些CLE,ALE引脚电平控制操作都通过Nand Flash控制器帮我们搞定了。如果没有Nand控制器,那么这些工作都需要我们编程完成。

①发地址         NFADDR=地址值

②发数据         NFDATA=数据值

③发命令         NFCMMD=命令值     

④读数据         val=NFDATA

存储芯片的编程一般可分为这几个过程:1、初始化;2、识别;3、读;4、写;5、擦除;

而对于我们的Nand编程,则:

1、Nand芯片的Nand Flash控制器

2、读取ID

3、一次读一个页

4、一次写一个页

5、一次擦除一个块(页是块的n倍)

nand的底层程序框架可以分为两部分:一部分是通用的协议层,另一部分是和单板硬件相关。比方说:xxx函数知道发出90命令,接着再发出0地址,然后读出的第一个数据是厂家id,第二个数据是设备id。这是所有nand都有的一个特点,但是至于怎样发命令,怎样发地址等等,这些都是协议层中的代码 使用底层提供的函数 去实现(后面分析代码可以知道,其实就是构造好mtd_info结构体提供给协议层使用,里面含有各种底层函数,如果我们没有去定义新函数,那么将会使用默认函数去发命令,发地址...)。所以我们应该重点去修改,查看底层相应的功能函数,比方说:选中、发命令、发地址、发数据、读数据、判断状态。另外,由于2440和2410的寄存器存在差异性,所以,在程序中当涉及寄存器操作时候,一定要认真查看相应的手册,确定操作无误。

2、开发文件对象2410 -> 2440

在drivers\mtd\nand\目录下,将s3c2410_nand.c复制为s3c2440_nand.c,并修改该目录下的Makefile

在include\configs\smdk2440.h中修改2410对应的宏为2440

3、修改分析代码

uboot启动输出信息中“NAND: 0 MiB”,在工程中搜索“NAND: ”,定位common\board_r.c的initr_nand()函数,并通过两次的函数嵌套调用,最终在nand_init_chip函数确定了Nand的大小并输出。

base_addr=0x4E000000(nand的NFCONF寄存器地址)。nand的IO读写地址会在 board_nand_init 函数中重新设置。

在drivers\mtd\nand\s3c2440_nand.c的 board_nand_init 函数中

nand_reg指向Nand控制器的第一个寄存器NFCONF(0x4E000000),Nand的io读写地址是在该函数中被重新设置为NFDATA的地址。nand->select_chip所指向的片选函数为NULL,那么将会在drivers\mtd\nand\nand_base.c的函数nand_set_defaults 中被设置为使用默认函数。这是底层相关的函数,默认提供的函数可能并不符合我们的要求,后面有必要去修改它。

tacls twrph0 twrph1简单来说就是脉冲时间参数,需要配置NFCONF寄存器。当发生①②③④操作时候,就让它按照设置好的时间参数去控制Nand Flash给它发地址、发命令等等,因为我们外接的Nand Flash可能是不同种类的,所以就要按照特定的类别要求去设置好,让控制器发出符合该型号nand的时序控制信号。对比2440和2410手册我们可以知道,操作的位已经发生变化,需要重新来设置。


翻阅2440手册,找到对应的时间参数配置寄存器NFCONF,以及Nand Flash的内存定时说明图:

nWE:低电平有效。

翻阅《K9F2G08U0C》Nand手册,找到该型号nand在特定电压(3.3v)下的最小时间参数:

我们可以借助命令锁存周期说明图(Command Latch Cycle)来理解:

根据AC Timing表可以知道,t(CLS)和t(WP)的最小值为12us,所以tacls最小为0。

前面篇节已经设置HCLK=100Hz,根据该手册中指定的最小时间以及2440中的参数公式可以反推出,twrph0 twrph1的最小设置分别为:1 , 0。所以,使用程序提供的默认参数完全没有问题。


在该函数对nand控制器的初始化中并没有读写操作Nand Flash,为了避免误操作,可以取消选中。重新添加使能nand,以及初始化ECC编码/解码器,我们需要启用ecc校验 (软件生成),以保证数据安全可靠。因为Nand存在坏块缺陷,无法避免。数据写到页中,并且生成ECC校验码存放在页后紧跟的OOB区;当开始读数据的时候,先读页内容,生成所读页的ECC校验码,和原先存放在OOB区的校验码进行比较,如果匹配表明数据没有问题,否则,会通过某种方法去修改,使得数据正确。

所以,需修改drivers\mtd\nand\s3c2440_nand.c中定义的宏如下,并且将程序中的宏名字替换为2440,再注释掉137行,并模仿nfconf,配置好nfcont:

接着分析s3c24x0_hwcontrol函数,在66~71行,2410操作的是NFCONF寄存器选中/取消片选,而2440操作的是NFCONT寄存器,所以,需要修改寄存器和宏。

至此,我们通过分析观察可以知道,在 board_nand_init 函数中,主要就是构造好nand->chip结构体,设置一些底层函数,这些函数将会在nand_scan函数中排上用场。

在nand_scan调用nand_scan_ident函数,设置默认函数。然后在nand_get_flash_type函数中选中片选,复位,发出90命令,再发出0地址,接着读取id。上面也曾分析过,这是协议层函数,调用的是应该是我们底层提供给它的片选函数,默认提供的函数并不能满足我们的要求,所以有必要重新写一个片选函数。

先修改片选函数:重写一个片选函数,而不是在默认函数基础上去修改,虽然可以这样做,但是为了代码规整,不提倡。

另外,因为之前并没有设置chip->cmdfunc,所以,发命令使用的也是默认函数nand_command。

跟踪进函数nand_command,发现所谓的发命令,发地址就是调用chip->cmd_ctrl。

而chip->cmd_ctrl在函数board_nand_init就被指向了s3c24x0_hwcontrol,所以s3c24x0_hwcontrol还可以用来发命令,发地址(行地址:哪一页  列地址:页内偏移),那么该函数怎么区分是发命令?还是发地址?还是操作片选信号?

阅读nand_command函数,发现就是通过传入s3c24x0_hwcontrol的第三个参数ctrl来区分。

重现分析s3c24x0_hwcontrol是怎样实现发命令和发地址的:(原来代码)

查阅2410芯片手册,发现0x4E000008是NFADDR寄存器,0x4E000004是NFCMD寄存器。

而在2410芯片手册,发现0x4E00000C是NFADDR寄存器,0x4E000008是NFCMD寄存器。

所以有必要修改对应宏,类似上面去修改就行了,不再重复。此处仅放出修改后的代码:

同样地,查看前面代码也发现读写函数也是使用默认的提供函数。那么busw到底有没有设置呢?

一直追查,发现busw就是默认设置函数的第二个参数,而chip->options并没有被设置,所以使用的读写函数是基于8位的,符合我们的Nand Flash接口情况,所以不必去修改了。

5、总结&&结果

我们主要修改了底层的发命令,发地址,片选操作函数。在修改的过程最大限度保留了代码的编写作风,当然,你也可以直接操作寄存器达到目的。最后,一定要结合nand_command命令处理函数思考,在当发生命令调用时,代入整个流程分析,这里就不展开讨论了。另外,若要打印调试信息,可以在drivers\mtd\nand\s3c2440_nand.c开头定义:

虽然会有警告信息,但是不能忽略_DEBUG。

编译,烧写,测试(关调试):

编译,烧写,测试(开调试):

4、遗留问题

写操作有点问题,但是目前不知道问题到底出在哪了。

①开调试打印,在uboot菜单中输入:

SMDK2440 # nand erase 0 80 (没问题)

SMDK2440 # nand write 0 0 80 (输出错误信息如下)

SMDK2440 # nand write 0 0 80

NAND write: device 0 offset 0x0, size 0x80
hwcontrol(): 0xffffffff 0x81 
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80 //将参数代入hwcontrol()分析,功能是:取消选中
hwcontrol(): 0xffffffff 0x81 //选中片选
hwcontrol(): 0x70 0x83  //选中片选,发命令0x70 读状态
hwcontrol(): 0xffffffff 0x81 //选中片选
hwcontrol(): 0x80 0x83  //选中片选,发命令0x80 
hwcontrol(): 0x00 0x85  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0xffffffff 0x81 
hwcontrol(): 0x10 0x83  //选中片选,发命令0x10 启动写操作 单位:1页
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x70 0x83  //选中片选,发命令0x70 查询写操作是否完成
hwcontrol(): 0xffffffff 0x81
dev_ready     //检查状态引脚
hwcontrol(): 0xffffffff 0x80 
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x81 
hwcontrol(): 0x00 0x83  //选中片选,发命令0x00,正好对应读操作的第一个访问周期命令
hwcontrol(): 0x00 0x85  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发命令0x00
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x30 0x83 //发命令0x30  正好对应读操作的第二个访问周期命令
hwcontrol(): 0xffffffff 0x81
dev_ready
dev_ready
hwcontrol(): 0x05 0x83   //结束写操作,和读一样。
hwcontrol(): 0x828 0x85
hwcontrol(): 0x08 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0xe0 0x83
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
NAND write to offset 0 failed -5
 0 bytes written: ERROR

写操作:(操作单位:/1页)

读操作:(操作单位:/1页)

SMDK2440 # nand read 0x30000000 0 80

②关调试打印,在uboot菜单中输入:

附:命令集表

5、遗留问题“NAND write to offset 0 failed -5”解决方法

由于nand存在位反转缺陷,当写入页数据的时候,需要根据页数据生成ECC校验码,并写入当前页后面的oob区。当开始读该页的数据的时候,由于nand的位反转缺陷,可能导致读出的数据某位发生了错误。所以需要继续读出oob区的校验码,根据校验码修正数据中的错误位。

根据输出的错误信息,在工程寻找出错位置函数,发现于cmd\nand.c的nand_write_skip_bad函数,返回值错误。

跟踪进该函数,加入打印调试信息,根据输出信息发现其实是在调用nand_verify函数验证写入数据和缓冲区的数据的过程中出错,导致写入不成功。校验码其实早在nand_write的时候就已经写入oob区了,读取数据的时候自然会根据oob区的校验码修改数据,为什么还要和缓冲区的数据校验呢?(缓冲区的数据其实就是源数据)另外,我发现在旧版的uboot中,并没有这个判断选项,所以暂且先将其关闭,测试是否能成功。(测试证明没问题)

修改前uboot输出:(添加详细注释)

SMDK2440 # nand write 0 0 800
argv[0]=nand  //该参数是我添加打印的,源码没有
argv[1]=write
argv[2]=0
argv[3]=0
argv[4]=800

NAND write: device 0 offset 0x0, size 0x800
in else raw size=2048,off=0,dev=0  //自行添加打印的,源码没有
after else raw size=2048,off=0,dev=0  //自行添加打印的,源码没有
before nand_write_skip_bad addr=0,off=0,rwsize=0,maxsize=2048,ret=0  //自行添加打印的,源码没有
inskip blocksize=131072,length = 2048,offset=0  //自行添加打印的,源码没有
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
inskip actual=0,need_skip=0  //自行添加打印的,源码没有
need_skip = 0  //自行添加打印的,源码没有
inskip before write buffer=0,length = 2048,offset=0,rval=0  //自行添加打印的,源码没有
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x70 0x83
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x80 0x83
hwcontrol(): 0x00 0x85
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x10 0x83
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x70 0x83
hwcontrol(): 0xffffffff 0x81
dev_ready
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
inskip after write buffer=0,length = 2048,offset=0,rval=0  //自行添加打印的,源码没有
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x00 0x83
hwcontrol(): 0x00 0x85
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x30 0x83
hwcontrol(): 0xffffffff 0x81
dev_ready
dev_ready
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
inskip after verify buffer=0,length = 2048,offset=0,rval=-5   //自行添加打印的,源码没有
NAND write to offset 0 failed -5
after nand_write_skip_bad addr=0,off=0,rwsize=0,maxsize=0,ret=0   //自行添加打印的,源码没有
 0 bytes written: ERROR

修改后uboot输出:(添加详细注释)

参考资料:

《嵌入式Linux应用开发完全手册》 韦东山著

《S3C2440A_UserManual_Rev13》

《K9F2G08U0C(NAND FLASH)》

 

 

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
U-Boot是一种广泛使用的开源引导加载程序,用于在嵌入式系统中启动操作系统。移植U-Boot到特定的硬件平台上需要一定的技术指导。以下是一个简要的U-Boot移植详细教程: 1. 准备工作: - 获取目标硬件平台的技术资料,包括处理器型号、芯片组、存储器配置等相关信息。 - 下载U-Boot源代码,通常可以从U-Boot官方网站或开源软件社区获取。 2. 配置U-Boot源代码: - 进入U-Boot源代码所在目录,执行配置命令,如"make menuconfig"。 - 在配置界面中选择目标硬件平台的相关配置项,包括处理器架构、存储器映射、串口配置等。 3. 修改U-Boot源代码: - 根据目标硬件平台的技术资料,修改U-Boot源代码中与硬件相关的部分,如引导设备、时钟设置、外设控制等。 4. 编译U-Boot: - 执行编译命令,如"make",根据目标平台架构进行交叉编译,生成可执行的U-Boot镜像文件。 5. 烧写U-Boot镜像: - 根据目标硬件平台的烧写工具和流程,将生成的U-Boot镜像烧写到目标设备的启动介质中,如闪存、SD卡等。 6. 测试和调试: - 在目标设备上启动U-Boot,连接串口终端工具,观察启动信息和日志输出,确保U-Boot正常工作。 - 进行各种功能测试,如引导其他操作系统、网络启动等,确保U-Boot在目标硬件平台上能够正常工作。 通过以上步骤,就可以完成U-Boot移植工作。当然,具体的移植过程还会受到目标硬件平台的特殊性和软件环境的影响,这只是一个大致的教程概述,具体操作过程中还需要根据实际情况进行调整和优化。同时,可以参考U-Boot官方文档、社区论坛和其他技术资料,进一步了解和掌握U-Boot移植技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值