Flash基础入门之U-boot(NOR Flash)
小标 2018-07-09 来源 : 阅读 1298 评论 0

摘要:本文主要向大家介绍了Flash基础入门之U-boot(NOR Flash),通过具体的内容向大家展现,希望对大家学习Flash基础入门有所帮助。

本文主要向大家介绍了Flash基础入门之U-boot(NOR Flash),通过具体的内容向大家展现,希望对大家学习Flash基础入门有所帮助。

1.       顶层目录下的Makefile

按照配置顺序:

davinci_config :    unconfig

            @./mkconfig $(@:_config=) arm arm926ejs davinci

执行配置命令:

            make  davinci_config

通过./mkconfig脚本会生成include/config.mk的配置头文件。

内容如下:

ARCH   = arm

CPU    = arm926ejs

BOARD  = davinci

因此,我们可以得知,该u-boot工程的目录路径。

board/davinci/下存放的是达芬奇的电路相关内容

cpu/arm926ejs存放CPU相关

lib_arm/

include/arm-asm/

include/configs/davinci.h  ------包含了基本所有的板子相关宏定义,默认的参数列表也在

 

在make davinci_config之后,再次查看最上层的Makefile,我们发现

include  include/config.mk

export    ARCH  CPU  BOARD ...

上述的内容就是将板子的配置内容导出设置为环境变量

 

Makefile的编译选项 和编译规则 都放在顶层目录的config.mk文件中定义。各种体系结构通用的规则直接在这个文件中定义

在Makefile中,包含了一些前缀

ifeq ($(ARCH),arm)

CROSS_COMPILE = arm_v5t_le-

endif

export  CROSS_COMPILE

 

接着是处理器相关的目标文件

根据上述的CPU=arm926ejs得知包含的路径为

OBJS  = cpu/$(CPU)/start.o

。。。

LIBS=lib_generic/libgeneric.a   定义LIBS依赖目录,将目标文件链接成*.a文件(静态库)

 

u-boot生成镜像的Makefile生成目标

#########################################################################

 

ALL = u-boot.srec u-boot.bin System.map

 

all:            $(ALL)

 

u-boot.hex:     u-boot

                $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@

 

u-boot.srec:    u-boot

                $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

 

u-boot.bin:     u-boot

                $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

 

u-boot.img:     u-boot.bin

                ./tools/mkimage -A $(ARCH) -T firmware -C none \

                -a $(TEXT_BASE) -e 0 \

                -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' include/version.h | \

                        sed -e 's/"[     ]*$$/ for $(BOARD) board"/') \

                -d $< $@

 

u-boot.dis:     u-boot

                $(OBJDUMP) -d $< > $@

 

u-boot:         depend $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

                UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*__u_boot_cmd_.*/-u\1/p'|sort|uniq`;\

                $(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) \

                        --start-group $(LIBS) --end-group $(PLATFORM_LIBS) \

                        -Map u-boot.map -o u-boot

上述中Makefile缺省的编译目标为all, 包括u-boot.srec、u-boot.bin 、System.map。 u-boot就是通过ld命令按照u-boot.map地址表将目标组成u-boot

 

 

2.       开发板相关配置

除了编译过程Makefile以外, 还要在程序中为开发板定义配置选项或者参数。这个头文件就是include/configs/davinci.h.

如下:配置CPU

#define  CONFIG_ARM926EJS  /* This is an arm926ejs CPU core          */

#define CONFIG_SYS_CLK_FREQ   297000000 /* 时钟Arm Clock frequency */

…..

 

3.       编译结果

通过上面的了解,先执行 make davinci_config

然后执行make即可

(清除执行make clean或者make distclean)

 

4.       添加u-boot命令

5.       U-boot启动过程分析

运行内存地址 定义:

board/davinci/config.mk

board/davinci/config.mk:26:TEXT_BASE = 0x81080000

           

.text           0x81080000    0x13504

 cpu/arm926ejs/start.o(.text)

 .text          0x81080000     0x3c0 cpu/arm926ejs/start.o

                                     0x81080000    _start

 

上面是内存的运行起始地址

0x81098424                __bss_start = .

后面是代码段和堆栈段

.bss           0x810a0d1c        0x4 lib_arm/libarm.a(armlinux.o)

                0x810a0d20                _end = .

 

 

由board/davinci/u-boot.lds看到

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS

{

        . = 0x00000000;

        . = ALIGN(4);

        .text   :

        {

          cpu/arm926ejs/start.o (.text)

          *(.text)

        }

。。。。。。。。。。。。。

该入口由start.o开始执行

cpu/arm926ejs/start.S

…..

_start_armboot: .word  start_armboot

….

该函数在lib_arm/board.c中实现

1.       start_armboot(void)函数

 

__asm__ __volatile__("": : :"memory");   内存屏蔽:

__asm__ 用于在此处插入汇编语句

__volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即,原原本本的按照原来的样子处理这里的汇编。

memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。

"":::表示这是个空指令

 

注意:我们很多的配置都根据make davinci_config后,查看include/configs/davinci.h

如:配置#define CFG_MALLOC_LEN                   (0x10000 + 128*1024)  /* malloc () len */

该说明,我们u-boot占用flash的大小是128K+64K=192K

需要将u-boot写在从0开始到0x30000为止。

 

start_armboot入口进入后主要进行就是一些初始化的工作。

认识一些全局变量:

a.内存占用初始化大小

mem_malloc_start = dest_addr;

mem_malloc_end = dest_addr + CFG_MALLOC_LEN;  // 0x10000+128*1024=192k

 

信息:

mem_malloc_start=81050000, mem_malloc_end=81080000, monitor_flash_len=   18484

 

b. 初始化函数功能指针

init_sequence----(cpu_init, board_init, interrupt_init, env_init, init_baudrate,……..)

具体的查看达芬奇手册的96页的表: PSC寄存器映射表

   

电源和睡眠模式控制

   

#define PSC_ADDR    

   

0x01C41000

   

后面还包含了电源管理命令和状态寄存器

   

cpu_init() -----------是否开启ARM的快速中断模式,如果开始,那么在起始内存前面的128byte加上中断向量表。

board_init()-----------主要是电源管理和睡眠模式管理,上电开启所有模块的电源。

 

 

 

 

 

目录

 

添加达芬奇NOR flash读写程序... 5

(Flash init初始化第一步)发送命令读取厂商设备ID.. 7

(Flash初始化第二步)擦除Flash的某个块... 10

(Flash初始化第三步) 写入数据Flash某个块... 11

(Flash初始化第四步)读取Flash某个块的数据... 12

 

 


添加达芬奇NOR flash读写程序

关键代码文件

common/flash.c

board/davinci/flash.c

include /configs/davinci.h

 

先查看一下命令

 

NOR Flash(s29gxxxn_00_a6_e)的硬件电路接法是地址和数据分开的,类似于DDR的接法,但是和DDR不同的是,这个也是需要命令的形式来进行传输,

 

如下图:

 

 

 

NOR Flash的读写比NAND Flash的要简单许多。

我们这里达芬奇接的NOR Flash EMIF接口的基地址是0x0200 0000,并且是16bit的数据位宽度,读写如下:


(Flash init初始化第一步)发送命令读取厂商设备ID

 

 

设置flash_info_t信息中的ID, sector数量,以及flash size大小

1. NOR Flash读取生厂商ID和设备ID的步骤

a)        执行相应的命令序列:(参考 NOR Flash的数据手册,见71page)

=è向基地址发送命令

                    *(0x02000000 + 0x0555)=0x00AA;

         *(0x02000000 + 0x02AA)=0x0055;

                     *(0x02000000 + 0x0555)=0x0090;

也可以定义一个基地址:volatile unsigned short *addr;

                    将addr变量赋值为0x02000000

                     上面相当于addr[0x0555]=0x00AA;

                                               

b)        当在基地址写了上面的3个命令后,就可以开始读取数据

上面的命令是读取生产商的ID:  manufacturer ID

(u16)mnfID=*(0x02000000+0x0);

这里我们读到的值为1

读到该值后,我们赋值一个初始值;

flash_info_t   info;

info->flash_id = 0x0000000; // (FLASH_MAN_AMD) AMD的flash产商

 

c)         读取Device ID:

(u16)devID=*(0x0C000000+0x1);

(u16)devID3=*(0x0C000000+0x0e);

 

(u16)devID4=*(0x0C000000+0x0F);

 

这里我们利用这个devID(switch ((FPW)addr[FLASH_ID2]))来判断信息

#if 1 //add by jk      

printf("DeviceID=%8x\n", (FPW)addr[FLASH_ID2]);

//这里我读到得是227e

#endif

 

好,看看下面的这些定义:

看来这个ID是一个家族,需要多个ID来识别

#define AMD_ID_MIRROR   0x227E227E     /* 1st ID word for MirrorBit family */

#define AMD_ID_DL640G_2         0x22022202     /* 2nd ID word for AM29DL640G  at 0x38 */

#define AMD_ID_DL640G_3         0x22012201     /* 3rd ID word for AM29DL640G  at 0x3c */

#define AMD_ID_LV640U_2         0x220C220C    /* 2nd ID word for AM29LV640M  at 0x38 */

#define AMD_ID_LV640U_3         0x22012201     /* 3rd ID word for AM29LV640M  at 0x3c */

#define AMD_ID_LV640MT_2 0x22102210          /* 2nd ID word for AM29LV640MT at 0x38 */

#define AMD_ID_LV640MT_3 0x22012201          /* 3rd ID word for AM29LV640MT at 0x3c */

#define AMD_ID_LV640MB_2 0x22102210         /* 2nd ID word for AM29LV640MB at 0x38 */

#define AMD_ID_LV640MB_3 0x22002200         /* 3rd ID word for AM29LV640MB at 0x3c */

#define AMD_ID_LV128U_2 0x22122212    /* 2nd ID word for AM29LV128M  at 0x38 */

#define AMD_ID_LV128U_3 0x22002200    /* 3rd ID word for AM29LV128M  at 0x3c */

#define AMD_ID_LV256U_2 0x22122212    /* 2nd ID word for AM29LV256M  at 0x38 */

#define AMD_ID_LV256U_3 0x22012201    /* 3rd ID word for AM29LV256M  at 0x3c */

#define AMD_ID_GL064M_2 0x22132213  /* 2nd ID word for S29GL064M-R6 */

#define AMD_ID_GL064M_3 0x22012201  /* 3rd ID word for S29GL064M-R6 */

那么我们添加一个ID3 ,ID4

#if 1 //add by jk      

printf("DeviceID2=%4x , ID3=%4x,ID4=%4x\n", (FPW)addr[FLASH_ID2],(FPW)addr[FLASH_ID3],(FPW)addr[FLASH_ID4]);

//这里我读到得是227e ,后面的是 2210 ,2200

#endif

通过条件判断

flash_info_t   info;

info->flash_id

info->flash_id += FLASH_S29GL064A;

 

info->flash_id += FLASH_S29GL064A;   //iD

                    info->sector_count = 135;    //135 sector

                    info->size = 0x00800000;   //8M

                    //前面8k*8=64K

                    for(i =0; i< 8; i++) 

                    {

                             info->start[i] = (ulong)addr + i*0x2000;

                    }

                    //后面64K*(135-8)=64K*128=8M大小,每个sector是 64K

                    for (i = 8; i < info->sector_count; i++)

                    {

                             info->start[i] = (ulong)addr + 0x10000 * (i-7);

                    }

                    break;

 

d)        获得上面的信息后,flash复位,设置为读模式即可

static void flash_reset(flash_info_t *info)

{

FPWV *base = (FPWV *)(info->start[0]);

 

/* Put FLASH back in read mode */

if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL)

           *base = (FPW)0x00FF00FF;  /* Intel Read Mode */

else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD)

           *base = (FPW)0x00F000F0;  /* AMD Read Mode */

}

可以说,flash的信息是我们自己根据flash硬件连接的手册自行赋值设置的。

在上述中,读模式也就是NOR Flash的复位模式,向某个块地址写入0x00FF或者0x00F0就可以回到Read array模式。

手册中经常用Read Array Mode来表示读模式,要进入该模式只要

*base=(FPW)0x00F000F0;即可(intel 写入 0x00FF即可)

 

 


(Flash初始化第二步)擦除Flash的某个块

2. 以块为单位擦除NOR Flash的步骤

操作原理:NOR Flash也是按块sector来进行擦除的,该块大小是64K,先写命令到0x0555和0x02aa,然后检查该块的数据是否为0xFFFF

环境:  假设我们用的是8M大小的NOR flash, 那么根据64K大小来分,一共有135个sector,因此我们要擦除2620000~ 263FFFF的flash, 即 2个sector,105和106两个块(要注意到,最前面的64k,分为8个sector,每个sector大小是8k, 后面的从第9个sector开始才是64K大小的,并且用flinfo查看属性,可以看到前面的8K的8个sector是只读的,不能改的。8M=128*64k , 但是这里,8K*8+ 127*64k=8M,因此一共135个sector.)

           擦除的命令就是向该块地址写命令:(查看手册也可以知道)

*(0x02000000+0x0555)= (unsigned short)0x00AA00AA;

*(0x02000000+0x02aa)= (unsigned short)0x00550055

                             *(0x02000000+0x02aa)= (unsigned short)0x00800080   --擦除模式

                             *(0x02000000+0x02aa)= (unsigned short)0x00AA00AA

                             *baseaddr(要擦除的sector地址)= 0x00300x0030

                             while((*(volatile unsigned short int )baseaddr) != 0xFFFF);

                             printf(“擦除结束!\n”);

                            

达芬奇代码:

FPWV *base;            /* first address in bank */

 

                    base = (FPWV *)(info->start[0]);

                    base[FLASH_CYCLE1] = (FPW)0x00AA00AA;      /* unlock */

                    base[FLASH_CYCLE2] = (FPW)0x00550055;         /* unlock */

                    base[FLASH_CYCLE1] = (FPW)0x00800080;         /* erase mode */

                    base[FLASH_CYCLE1] = (FPW)0x00AA00AA;      /* unlock */

                    base[FLASH_CYCLE2] = (FPW)0x00550055;         /* unlock */

                    *addr = (FPW)0x00300030;  /* erase sector */

                    while (*((vHwdptr)addr) != AMD_ERASE_DONE);

                    printf("done.\n");


(Flash初始化第三步) 写入数据Flash某个块

 

 

   *((vHwdptr)address_cs + AMD_CMD0_ADDR) = AMD_PROG_CMD0;

    *((vHwdptr)address_cs + AMD_CMD1_ADDR) = AMD_PROG_CMD1;

    *((vHwdptr)address_cs + AMD_CMD2_ADDR) = AMD_PROG_CMD2;

 

*(0x02000000+0x0555)= (unsigned short)0x00AA00AA;

*(0x02000000+0x02aa)= (unsigned short)0x00550055

                             *(0x02000000+0x02aa)= (unsigned short)0x00A000A0   --编程模式

将要写入的地址赋值数据

*psAddress = ulData;

这样其实就已经写入了,将ulData数据,写入了psAddress中

但是我们需要进行等待,判断是否已经写好了,写好后进行下个数据的写

While(1){

tmp = *psAddress; 用tmp变量来判断

if( (tmp & BIT7)  == (ulData & BIT7))

           {

                    break;

           }

}

上面的是最简单的判断,就是判断高位字节是否相等

 


(Flash初始化第四步)读取Flash某个块的数据

在软件复位后,直接读即可

手册中经常用Read Array Mode来表示读模式,要进入该模式只要

*base=(FPW)0x00F000F0;即可(intel 写入 0x00FF即可)

这样复位后,就可以读了。

 

看,添加打印信息:我们可以查看2020000开始放ENV环境变量参数开始的NOR Flash内容

添加代码:

addr=(FPW *)0x2020000;

                printf("0x2020000=%4lx\n",*(addr));

                 printf("0x2020001=%4lx\n",*(addr+1));

---前面的这4个byte是无效的

                 printf("0x2020002=%4lx\n",*(addr+2));

                printf("0x2020003=%4lx\n",*(addr+3));

                printf("0x2020004=%4lx\n",*((addr+4)));

                 //printf("0x2020005=%4lx\n",*((addr+5)));

                 printf("0x2020006=%4lx\n",*((addr+6)));

                 printf("0x2020008=%4lx\n",*((addr+8)));

                 printf("0x202000a=%4lx\n",*((addr+0x0a)));

 

打印结果:

0x2020000=5e92

0x2020001=d070

 

0x2020002=6f62

0x2020003=746f

0x2020004=6564

0x2020005=616c

0x2020006=3d79

0x2020008=6162

0x202000a=6172

排放的顺序:

62 6f 6f 74 64 65 6c 61 79 3d

B  o o  t  d e  l  a  y =

有这几个我们可以看出参数开始排放了

 

自己写自己的命令

tftp  8000000  u-boot.bin

看到大小为1865C

protect  off all

或者一段段的将protect  off  2020000  2030000

cp.b  80800000  2020000  1865c(<=20000 ,128K的16进制)

等待done结束

Erase  2020000 202FFFF  一个段擦除

 

本文由职坐标整理并发布,了解更多内容,请关注职坐标常用软件Flash频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程