葡京娱乐总站平台25.Linux-Nor Flash叫(详解)

转: http://jm.taobao.org/2013/10/11/1036/

1.nor硬件介绍:

上周在线上系发现了片独bug,值得记录下寻找的经过以及因。以后如还有查找bug比较起价的更,我为会连续享受。

 葡京娱乐总站平台 1

率先只bug的原初,是在线上日志发现一个累打印的挺——java.lang.ArrayIndexOutOfBoundsException。但是却不曾仓库,只生同一履一行的ArrayIndexOutOfBoundsException。没有仓库,不懂得老是自从什么地方抛出来的,也就算未克找到题目的起源,更讲不达解决。题外,工程师于为此log4j记录错误非常的时刻,我见状成千上万总人口如此用(假设e是雅对象):
log.error(“发生误:”+e);
或者:
log.error(“发生错误:”+e.getMessage());
这样的写法是不对,只记录了大的音信,而从未将堆积栈输出及日志,正确的写法是利用error的重载方法:
log.error(“xxx发生错误”,e);
这样才会于日记中总体地出口大堆栈来。如何勾勒好日志是任何一个话题,这里不进行。继续我们的找bug经历。刚才提到,我们线达日志一直出现一行错误信息ArrayIndexOutOfBoundsException却从没仓库,是咱们从没科学地描写日记也?检查代码不是的,这个题目实际上是跟JDK5引入的一个新特性有关,对于有些往往抛来的良,JDK以性会举行一个优化,在JIT重新编译后会抛出没有仓库的酷。在动server模式的当儿,这个优化是打开之,我们的服务器跑在server模式下同时jdk版本是6,因此在数抛出ArrayIndexOutOfBoundsException异常一段时间后优化开始起作用,只抛来从未仓库的坏信息了。

于规律图备受我们会见到NOR
FLASH有地址线,有数据线,它跟咱们的SDRAM接口相似,能直接读取数据,但是未可知像SDRAM直接写副数据,需要有指令才行

那怎么解决之题目为?因为是优化是以JIT重新编译后才从作用,因此等同开始扔来十分的早晚要时有发生堆栈的,所以可以翻较旧的日记,寻找有整机堆栈的很信息。但是出于我们的日记太特别,会定期去,我们的服务器也启动了要命丰富时,因此查找日志不是死靠谱的章程。
任何一个解决办法是临时性禁用这个优化,强制要求每次都要废除来有堆栈的怪。幸好JDK提供了挑项来关闭是优化,配置JVM参数
-XX:-OmitStackTraceInFastThrow
就可禁止这个优化(注意选择中的减号,加号是启用)。

1.1里边我们2440底地址线共有27彻底(LADDR0~26),为什么是27干净?

我们探寻了令机器,配置了这参数并再开一下。过了同见面便找到问题所在,堆栈类似这样
Caused by: java.lang.ArrayIndexOutOfBoundsException: -1831238
at
sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:436)
at
java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2081)
at
java.util.GregorianCalendar.computeFields(GregorianCalendar.java:1996)
at java.util.Calendar.setTimeInMillis(Calendar.java:1109)
at java.util.Calendar.setTime(Calendar.java:1075)
at java.text.SimpleDateFormat.format(SimpleDateFormat.java:876)
at java.text.SimpleDateFormat.format(SimpleDateFormat.java:869)
at java.text.DateFormat.format(DateFormat.java:316)
读者必定猜到了,这个题目是出于SimpleDateFormat的误用引起的。SimpleDateFormat的javadoc吃发生如此句话:
Date formats are not synchronized. It is recommended to create separate
format instances for each thread.
If multiple threads access a format concurrently, it must be
synchronized externally.
唯独好悲剧的是这词话是放在整个doc的末段给,在我看来,这句话应该在最前头才对。简单来说就是是SimpleDateFormat不是线程安全的,你要么每次都new一个来用,要么做加锁来并使用。

盖2440一并产生7独bank内存块,每个bank=128MB=(2^27)B,所以共有27绝望数据线

来问题的代码就是由工程师认为SimpleDateFormat的创导代价十分高,然后做了单map做缓存,所有线程共用这instance做format,同时没有开并。悲剧就生了。
此处就引出自身想干的次碰问题,在行使一个接近或措施的时,最好能详细地圈下该类的javadoc,JDK的javadoc是召开的百般好的,javadoc除了做证明外,通常还见面叫示例,并且会硌发出有关键问题,如线程安全性和平台移植性。

1.2胡Nor
Flash的地址线A0是对接在2440之LADDR1上?

说到底,我拿做个测试,到底以行使SimpleDateFormat怎么开才是极端好之章程?假设我们设兑现一个formatDate方法以日期格式化成”yyyy-MM-dd”的格式。
首先单主意是每次用都创一个instance,并调用format方法:
public static String formatDate1(Date date) {
SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”);
return format.format(date);
}

以Nor
Flash的数量共有16位,也不怕是每个地方保存了2B数额,而我们的2440每个地点是保存之1B数据,

仲单办法是才创造一个instance,但是于调用方法的当儿召开并:
private static final SimpleDateFormat formatter = new
SimpleDateFormat(“yyyy-MM-dd”);

比如:

public static synchronized String formatDate2(Date date) {
return formatter.format(date);
}
其三单道比较突出,我们吧每个线程都缓存一个instance,存放于ThreadLocal里,使用的下打ThreadLocal里取就可了:
private static ThreadLocal<SimpleDateFormat> formatCache = new
ThreadLocal<SimpleDateFormat>() {

当2440访问0X00地址时,就见面读取到Nor上0地址的2B数据,然后2440之内存控制器会根据0x00来找到低8各类字节,并回到给CPU,

@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(“yyyy-MM-dd”);
}

当2440走访问0x01地址时,由于2440的LADDR0线未连接,所以要看Nor的0地址上之2B数据,然后内存控制器会根据0x01来找到高8各类字节,并返给CPU

};

1.3
nand和nor区别:

public static String formatDate3(Date date) {
SimpleDateFormat format = formatCache.get();
return format.format(date);
}
然后我们测试下三独主意并发调用生之习性并做一个比,并作100单线程循环调用1000万不良,记录耗时。我们设置了JVM参数:
-Xmx512m -XX:CompileThreshold=10000
设置堆最特别吗512M,设置当一个办法让调用1万涂鸦的时刻就深受JIT编译。测试的结果如下:

nor flash以标价达到比nand贵,且容量非常小
,擦除和描绘多少都磨蹭,好处在接口简单,稳定,无位反转,坏块,常用来保存要数据,而nand
flash常用于保存好容量数据

 

于2440着凡是经硬件开关来设置OM0为Nand启动或Nor启动,如下图所示:

 

 葡京娱乐总站平台 2

 

OM0切实参数如下所示,其中2440的OM1逗脚默认接地

 

 葡京娱乐总站平台 3

 

对nand启动:OM0交接地,nand
flash的起来4KB会自动地为加载到2440放的SRAM缓存器中,就可以直接读写

第1次测试

于nor启动:OM0连缀大,2440看的内存就是nor
flash,可以直接读,但是不能够一直写

第2次测试

 

第3次测试

2.nor
flash命令如下所示(参考MX29LV160DBTI.pdf)

 

 葡京娱乐总站平台 4

formatDate1

其中word是针对16位nand,byte针对8位nand.

50545

由我们2440之flash型号是MX29LV160DB,所以设备ID为0x2249

49365

 

53532

2.1
比如,当我们如果program(往0x20地址写入0xff数据)时

 

亟需以下3步:

formatDate2

1.发送解锁地址:

10895

往nor地址0x555写入0xAA

10761

往nor地址0x2AA写入0x55 

10673

2.发送命令:     

 

往nor地址0x555写入0xA0               
//进入program模式

formatDate3

3.写数据:

10386

往nor地址0x20(PA)写入0xff(PD)         
//往0x20写入0xff

9919

(接下便会一直是program模式,执行reset模式就是得以退)

9527

2.2拖欠NOR有少数栽标准,
jedec, cfi(common flash interface) 

(单位:毫秒)

jedec

起结果来拘禁,方法1极致缓慢,方法3极致抢,但是就算是最缓慢的法子1啊堪达标每秒钟200 20万软的调用量,很少出系统能够达标这个量级。这个点异常麻烦成您系统的瓶颈所在。从自家的角度出发,我会建议你用艺术1还是方法2,如果你追那么一些性能提升的话语,可以设想用智3,也就是为此ThreadLocal做缓存。

就是和nandflash的一样,通过读ID来匹配linux内核中drivers/mtd/chips/jedec_probe.c里的jedec_table[]往往组,来确定norflash的次第参数(名称、容量、位宽等),如下图所示:

总下本文找bug经历想表达的几乎沾想法:
(1)正确地打印错误日志
(2)在server模式下,最好都安装-XX:-OmitStackTraceInFastThrow
(3)使用类或者措施的时,最好能详细看下javadoc,很多题目都能找到答案
(4)使用SimpleDateFormat的时候如果专注线程安全性,要么每次new,要么做同,两者的性质有差异,但是这差距颇麻烦成您的特性瓶颈。

 葡京娱乐总站平台 5

  • [0] =
    MTD_UADDR_0x5555_0x2AAA

意味着解锁地址也0x5555,0x2AAAM,其中数组[0],表示属于8各项flash,定义如下:

葡京娱乐总站平台 6

  •  CmdSet

运啊种命令,一般CmdSet=0xFFF0

  • .NumEraseRegions= 1

但发1个不等之扇区区域

  • ERASEINFO(0x10000, 64)

共有64只扇区,每个扇区都是64KB(0x10000)

cfi

不畏将这些参数保存在cfi模式下指定地点被,
往nor的0x55地址写入0x98,即可进入cfi模式,

cfi模式有令如下图所示:

 葡京娱乐总站平台 7

当我们于cfi模式下,比如:读取nor地址0x27高居的数目,便会念到nor的容量

正如图所示,之所以地址*2,是因为nor地址线A0接在咱们2440的A1上(退出cfi模式,使用复位命令即可)

葡京娱乐总站平台 8

诵读到0X15,0x15=21,如下图,刚好对应我们原理图的21干净nor地址线,所以容量也2^21=2MB

 葡京娱乐总站平台 9

2.3为什么上图的A20滋生脚没连通?

于2440来讲话,因为此时之A0~A19底容量刚好也2MB,与cfi模式下读取的数目一致,所以无接A20

 

3.接下来便来分析如何勾勒norflash驱动

3.1
先来回顾下前的nandflsh驱动:

nandflsh驱动会放在内核的mtd设备遭遇,而mtd设备接头怎么通过命令/地址/数据来操作nandflash,所以我们事先的nandflash驱动只兑现了硬件相关的操作(构造mtd_info,nand_chip结构体、启动nand控制器等)

一如既往地,norflash驱动也是坐落内核的mtd设备遭遇,mtd设备也懂得针对nor如何来读写擦除,只是不晓得norflash的位宽(数据线个数),基地址等,所以我们的norflash驱动同样如果兑现硬件相关的操作,供给mtd设备调用

 

3.2参考内核自带的nor驱动:drivers/mtd/maps/physmap.c

上其的init函数:

 葡京娱乐总站平台 10

发现报了少于个platform平台设备驱动,进入physmap_flash结构体中:

 葡京娱乐总站平台 11

发觉3只非定义之变量:

CONFIG_MTD_PHYSMAP_BANKWIDTH:
nandflash的字节位宽

CONFIG_MTD_PHYSMAP_START:nandflash的物理基地址

CONFIG_MTD_PHYSMAP_LEN:
nandflash的容量长度

立即3独变量是透过linux的menuconfig菜单配置出的,若自己填写入值,就不需要因此menuconfig菜单配置了

 

3.3搭下去我们便来安排基本,然后挂载这个根本自带的norflash驱动实验一番

3.4 首先make
menuconfig,配置方面3独变量,然后设为模块

-> Device
Drivers                

 -> Memory Technology Device (MTD)
support  

 -> Mapping drivers for chip
access                   //进入映射驱动


 

<M>
CFI Flash device in physical memory map       
  //将支持cfi的norflash设置为模块

  •    (0x0)
    Physical start address of flash mapping  // 设置物理基地址
  •   
    (0x1000000) Physical length of flash mapping  //
    设置容量长度,必须大于等于我nor的2MB
  •    (2)  
    Bank width in octets (NEW)                   //
    设置字节位宽,因为nor为16号,所以当2

 


3.5 make modules 编译模块

一般来说图所示,可以看physmap.c编译成.ko模块了

 葡京娱乐总站平台 12

3.6
然后在nfs目录下,启动开发板

一般来说图所示,insmod后打印了平错信息:

 葡京娱乐总站平台 13

如下图所示,可以看出创建了2独mtd0字符设备,一个mtd0片设备:

 葡京娱乐总站平台 14

 

4.接下来我们即便分析physmap.c,如何勾勒有norflash驱动之

个中physmap.c的probe函数如下

struct physmap_flash_info {
       struct mtd_info             *mtd;             //实现对flash的读写擦除等操作
       struct map_info            map;              //存放硬件相关的结构体
       struct resource             *res;
#ifdef CONFIG_MTD_PARTITIONS
       int                 nr_parts;
       struct mtd_partition      *parts;
#endif
};

static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };    //芯片名称

... ...
static int physmap_flash_probe(struct platform_device *dev)
{
       const char **probe_type;
       ... ...
       /*1. 分配结构体*/
       info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);

       /*2.设置map_info 结构体*/
    info->map.name = dev->dev.bus_id;                 //norflash的名字
       info->map.phys = dev->resource->start;          //物理基地址
       info->map.size = dev->resource->end - dev->resource->start + 1;       //容量长度
       info->map.bankwidth = physmap_data->width;                     //字节位宽
       info->map.virt = ioremap(info->map.phys, info->map.size);    //虚拟地址

       simple_map_init(&info->map);                   //简单初始化map_info的其它成员

       probe_type = rom_probe_types;
       /*3. 设置mtd_info 结构体 */
       /*通过probe_type指向的名称来识别芯片,当do_map_probe()函数返回NULL表示没找到*/
       /*当找到对应的芯片mtd_info结构体,便返回给当前的info->mtd */
       for (; info->mtd == NULL && *probe_type != NULL; probe_type++)       
       info->mtd = do_map_probe(*probe_type, &info->map); //通过do_map_probe ()来识别芯片


       if (info->mtd == NULL) {             //最终还是没找到芯片,便注销之前注册的东西并退出
              dev_err(&dev->dev, "map_probe failed\n");
              err = -ENXIO;
              goto err_out;
       }

       info->mtd->owner = THIS_MODULE;        



       /*4.添加mtd设备*/

       add_mtd_device(info->mtd);              

       return 0;



err_out:

       physmap_flash_remove(dev);                      //该函数用来注销之前注册的东西

       return err;

}

 

经过上面的代码和注释分析到,和咱们上等同节约之nandflash驱动相似,这里是安map_info
结构体和mtd_info结构体来成功的,当我们设本着norflash分区将以add_mtd_partitions()才行

其中当*probe_type==“cfi_probe”时:

就会见透过do_map_probe(“cfi_probe”,
&info->map)来鉴别芯片.

最终见面进drivers/mtd/chips/cfi_probe.c中的cfi_probe_chip()函数来上cfi模式,读取芯片信息

当*probe_type==”jedec_probe”时:

说到底见面进来drivers/mtd/chips/jedec_probe.c中的jedec_probe_chip
()函数来使用读ID命令,通过ID来配合配jedec_table[]数组.

之所以注册一个片设备驱动,需要以下步骤:

  • 1.
    分配mtd_info结构体和map_info结构体
    1. 设置map_info
      结构体
    1. 设置mtd_info
      结构体
  • 4.
    使用add_mtd_partitions()或者add_mtd_device()来创建MTD字符/块
    设备

 

5.接下来我们来参考physmap.c来协调写norflah驱动

代码如下:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>


static struct mtd_info        *mynor_mtd_info;
static struct map_info         *mynor_map_info;


static struct mtd_partition mynor_partitions[] = {
       [0] = {
        .name   = "bootloader",
        .size   = 0x00040000,
        .offset     = 0,
       },
       [1] = {
        .name   = "root",
        .offset = MTDPART_OFS_APPEND,
        .size   = MTDPART_SIZ_FULL,
       }
};

static const char *mynor_probe_types[] = { "cfi_probe", "jedec_probe",NULL};


static int mynor_init(void)
{
    int val;

/*1. 分配map_info 结构体和mtd_info结构体*/
    mynor_mtd_info=kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
    mynor_map_info=kzalloc(sizeof(struct map_info), GFP_KERNEL);

/*2. 设置map_info 结构体*/
    mynor_map_info->name="my_nor";
    mynor_map_info->phys=0x0;                          //物理地址
    mynor_map_info->size=0x1000000;                 //=16M,长度必须大于等于norflash的2M容量
    mynor_map_info->bankwidth=2;                     //16位宽
    mynor_map_info->virt = ioremap(0x0, mynor_map_info->size);   //虚拟地址
    simple_map_init(mynor_map_info);

/*3. 设置mtd_info 结构体*/
   mynor_mtd_info = do_map_probe("cfi_probe", mynor_map_info);
   if (!mynor_mtd_info)
   {
     mynor_mtd_info = do_map_probe("jedec_probe", mynor_map_info);
    }

    if (!mynor_mtd_info)
    {
    printk(“not available norflash !!!\r\n”);
    goto err_out;
    }
    mynor_mtd_info->owner=THIS_MODULE;

 

/*4. 使用add_mtd_partitions()或者add_mtd_device()来创建MTD字符/块 设备*/
       add_mtd_partitions(mynor_mtd_info,mynor_partitions,2);
       return 0;


err_out:
       iounmap(mynor_map_info->virt);                      //取消虚拟地址映射
       kfree(mynor_map_info);
       kfree(mynor_mtd_info);      
       return 0;
}


static  void mynor_exit(void)
{
    del_mtd_partitions(mynor_mtd_info);                  //卸载分区
    iounmap(mynor_map_info->virt);                      //取消虚拟地址映射
    kfree(mynor_map_info);
    kfree(mynor_mtd_info);       
}

module_init(mynor_init);
module_exit(mynor_exit);
MODULE_LICENSE("GPL");

 

6.挂载驱动试验

(一定要是在nor启动下挂载才行,因为2440采取nand启动时,是看不了nor的前4k地址)

insmod挂载驱动后,如下图所示:

 葡京娱乐总站平台 15

可看看创建了有限独分区“bootloader”,“root”,如下图所显示,可以视创建了2对准mtd字符/块设备

 葡京娱乐总站平台 16

6.1
接下便来针对root分区(mtd1)来考查(使用flash之前最好好擦除同不良)

步骤如下:

./flash_eraseall -j /dev/mtd1                      //使用mtd-util工具的flash_eraseal命令来擦除root分区(mtd1)

mount -t jffs2 /dev/mtdblock1 /mnt/                //使用mount挂载文件系统, -t:文件系统类型(type)

 

接通下去就好在/mnt目录下来任意读写文件了,最终见面保留在flash的mtdblock1块设备遭遇

(PS:可以参考内核自带的mtdram.c,里面是采取外存来模拟flash,
里面通过memcopy()等来实现对内存读写擦除)