DM6446 uboot分析
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_.*)/-u1/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,……..)
cpu_init()?-----------是否开启ARM的快速中断模式,如果开始,那么在起始内存前面的128byte加上中断向量表。 board_init()-----------主要是电源管理和睡眠模式管理,上电开启所有模块的电源。 目录 添加达芬奇NOR?flash读写程序 5 (Flash?init初始化第一步)发送命令读取厂商设备ID 7 (Flash初始化第二步)擦除Flash的某个块 10 (Flash初始化第三步)?写入数据Flash某个块 11 (Flash初始化第四步)读取Flash某个块的数据 12 添加新的命令 14 添加达芬奇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]))来判断信息 printf("DeviceID=%8xn",?(FPW)addr[FLASH_ID2]); //这里我读到得是227e 好,看看下面的这些定义: 看来这个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 printf("DeviceID2=%4x?,?ID3=%4x,ID4=%4xn",?(FPW)addr[FLASH_ID2],(FPW)addr[FLASH_ID3],(FPW)addr[FLASH_ID4]);? //这里我读到得是227e?,后面的是?2210?,2200 通过条件判断 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=%4lxn",*(addr)); ?????????????????printf("0x2020001=%4lxn",*(addr+1)); ---前面的这4个byte是无效的 ?????????????????printf("0x2020002=%4lxn",*(addr+2)); ????????????????printf("0x2020003=%4lxn",*(addr+3)); ????????????????printf("0x2020004=%4lxn",*((addr+4))); ?????????????????//printf("0x2020005=%4lxn",*((addr+5))); ?????????????????printf("0x2020006=%4lxn",*((addr+6))); ?????????????????printf("0x2020008=%4lxn",*((addr+8))); ?????????????????printf("0x202000a=%4lxn",*((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??一个段擦除 参考:NOR?Flash读写 http://hi.baidu.com/liang888%BA%C3/blog/item/81e330191d35b54942a9add1.html 添加新的命令关于命令的几个文件: include?/command.h common/command.c include/cmd_confdefs.h common/cmd_*.c 先看一下添加命令的宏: U_BOOT_CMD 定义在include/command.h中 #define?U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)?? cmd_tbl_t??__u_boot_cmd_##name?Struct_Section?=?{#name,?maxargs,?rep,?cmd,?usage,?help} 可以看到每一个命令定义一个cmd_tbl_t结构体,该结构体也定义在同个头文件中。 CFG_CMD_*?等的命令的配置定义在: Include/cmd_confdefs.h中 只有定义了相关的配置,在common/cmd_*.c命令函数才会有效 我们现在不创建一个新的cmd_*.c,而是在原来的基础上添加一个命令 例如:添加flash的强制写命令 添加在common/cmd_flash.c文件中 int?do_writeuboot?(cmd_tbl_t?*cmdtp,?int?flag,?int?argc,?char?*argv[]) { int?????rcode?=?0; printf("Add?by?jk?for?test?the?writebootn"); return?0; } 这个宏中的字符串信息是帮助信息:当使用help的时候就会打印出来 U_BOOT_CMD( writeboot,?CFG_MAXARGS,?1,do_writeuboot, "writeuboot???-?update?the?davinci?u-boot.bin?in?NOR?flashn" "?????????????argv[1]?--start?the?NOR?Flash?Address'n" "?????????????argv[2]?--start?the?Memory?Address'n" "?????????????argv[3]?--the?u-boot.bin?hex?size'n"?, ); 在do_writeboot函数中,仅仅就打印一条信息 因此在命令中,只要输入了writeboot,那么就打印这个信息 可以在程序中补充完整的是writeboot?–help等信息 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |