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_.*\)/-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()-----------主要是电源管理和睡眠模式管理,上电开启所有模块的电源。
目录
(Flash init初始化第一步)发送命令读取厂商设备ID 7
添加达芬奇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=%8x\n", (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=%4x\n", (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=%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 一个段擦除
参考: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 writeboot\n");
return 0;
}
这个宏中的字符串信息是帮助信息:当使用help的时候就会打印出来
U_BOOT_CMD(
writeboot, CFG_MAXARGS, 1, do_writeuboot,
"writeuboot - update the davinci u-boot.bin in NOR flash\n"
" 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等信息
DM6446 uboot分析的更多相关文章
- Linux之uboot分析与移植20160601
说一下uboot分析与移植: 1.下载.建立source insight工程.编译.烧写.如果无运行分析原因 tar xjf u-boot-2012.04.01.tar.bz2 cd u-boot-2 ...
- u-boot分析(十一)----MMU简单分析|u-boot分析大结局|学习规划
u-boot分析(十一) 通过前面十篇博文,我们已经完成了对BL1阶段的分析,通过这些分析相信我们对u-boot已经有了一个比较深入的认识,在BL2阶段大部分是对外设的初始化,并且有的我们已经分析过, ...
- u-boot分析(十)----堆栈设置|代码拷贝|完成BL1阶段
u-boot分析(十) 上篇博文我们按照210的启动流程,分析到了初始化nand flash,由于接下来的关闭ABB比较简单所以跳过,所以我们今天按照u-boot的启动流程继续进行分析. 今天我们会用 ...
- u-boot分析(九)----nand flash初始化|nand flash读写分析
u-boot分析(九) 上篇博文我们按照210的启动流程,分析到了初始化串口,由于接下来的取消存储保护不是很重要,所以我们今天按照u-boot的启动流程对nand flash初始化进行分析. 今天我们 ...
- u-boot分析(八)----串口初始化
u-boot分析(八) 上篇博文我们按照210的启动流程,分析到了内存初始化,今天我们继续按照u-boot的启动流程对串口的初始化进行分析. 今天我们会用到的文档: 1. 2440芯片手 ...
- u-boot分析(七)----内存初始化
u-boot分析(七) 上篇博文我们按照210的启动流程,分析到了时钟初始化,今天我们继续按照u-boot的启动流程对内存的初始化进行分析. 今天我们会用到的文档: 1. 2440芯片手 ...
- u-boot分析(六)----时钟初始化
u-boot分析(六) 上篇博文我们按照210的启动流程,分析到了关闭看门狗,今天我们继续按照u-boot的启动流程进行分析,今天我们会主要分析时钟的初始化. 今天我们会用到的文档: 1. ...
- u-boot分析(五)----I/D cache失效|关闭MMU和cache|关闭看门狗
u-boot分析(五) 上篇博文我们按照210的启动流程,对u-boot启动中的设置异常向量表,设置SVC模式进行了分析,今天我们继续按照u-boot的启动流程对以下内容进行分析. 今天我们会用到的文 ...
- u-boot分析(四)---设置异常向量表|设置SVC模式
u-boot分析(四) 通过前三篇的分析,我们对u-boot已经有了整体的认识和掌握,但是我们仍然对于其部分硬件是如何初始化的不太清楚,所以接下来几篇博文我将会对我们在http://www.cnblo ...
随机推荐
- 邮件报警(postfix)
postfix是Wietse Venema在IBM的GPL协议之下开发的MTA(邮件传输代理)软件.postfix是Wietse Venema想要为使用最广泛的sendmail提供替代品的一个尝试.在 ...
- spring中ref属性与<ref/>标签
在bean的内部引用另一个bean对象: 使用ref标签对其进行引用: <ref bean="viewResolver2"/> <bean id="vi ...
- BZOJ 2141: 排队 [CDQ分治]
题意: 交换序列中两个元素,求逆序对 做分块做到这道题...一看不是三维偏序嘛.... 作为不会树套树的蒟蒻就写CDQ分治吧.... 对时间分治...x排序...y树状数组... 交换拆成两个插入两个 ...
- [偏序关系与CDQ分治]【学习笔记】
组合数学真是太棒了 $CDQ$真是太棒了(雾 参考资料: 1.<组合数学> 2.论文 课件 很容易查到 3.sro __stdcall 偏序关系 关系: 集合$X$上的关系是$X$与$X$ ...
- BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]
3992: [SDOI2015]序列统计 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 1017 Solved: 466[Submit][Statu ...
- WPF 使用DMSkin for WPF 快速搭建漂亮的WPF程序
DMSkin-for-WPF是一个基于WPF的.Net WPF开源界面库,实现了无边框的WPF开发方案,内置部分控件模板. 你可以参照模板自行修改完善.(以下简称DFW). 核心 DFW实现了比较完美 ...
- java中的foreach用法及总结
增强for(part1:part2){part3}; part2中是一个数组对象,或者是带有泛性的集合. part1定义了一个局部变量,这个局部变量的类型与part2中的对象元素的类型是一致的. pa ...
- 共享数据的包含const
1.常引用:被引用的对象不能被更新 使用:const 类型名 &引用对象 如const int &a; 2.常对象:必须进行初始化,并且对象不能改变 使用:类名 const ...
- 训练 smallcorgi/Faster-RCNN_TF 模型(附ImageNet model百度云下载地址)
1. 下载训练.验证.测试数据和 VOCdevkit,下载地址: http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2 ...
- MySQL备份常用命令总结
MySQL备份常用命令总结 1.数据库和数据全部备份 mysqldump -uroot -pPassword -hlocalhost databasename > test.sqlmysqldu ...