如何在uboot 网络配置代码中进行内存配置

下次自动登录
现在的位置:
& 综合 & 正文
S3C6410开发全纪录(二)《如何计算内存大小,并在UBOOT中调整内存大小》 .
前章我们已经可以制作出用来启动的SD卡了,并将自己编译的UBOOT烧录到了SD卡中
这份UBOOT中的内存配置肯定和手头的开发板不一致,这里我们将搞清楚如何修改内存大小,并说清楚内存大小到底是如何计算的
一、关于内存大小的计算
1)硬件的型号,在飞凌的开发板中
128M内存 采用的是 K4X51163PC 可以看出来 这颗芯片是 总大小为512 bit 位宽 为16
256M内存 采用的是 K4X1G163PC 可以看出来 这颗芯片是 总大小为1G
bit 位宽 为16
2)贴片数量2,都是用2DDR,来构成32位
3)查看DDR的芯片手册
128M内存 总共有 4 个 BANK 行列地址线数量分别为 Row = 13 Col = 10
256M内存 总共有 4 个 BANK 行列地址线数量分别为 Row = 14 Col = 10
4)以256M内存为例,内存大小的计算为
每个BANK的存储单元个数 = (2^Row)*(2^Col) = 2^(Row+Col)
每个存储单元 可以存放 16 位 的数据
总容量 = 芯片个数 * 每个芯片的BANK数量 * 位宽 * 行地址线数量 * 列地址线数量 (这里的单位都是bit)
= 2*4*16*(2^14)*(2^10) = 2^(1+2+4+14+10)= 2^31 bit
转换为 字节 2^28
转换为兆 2^8 M = 256 M
这里需要说明的是 行列地址线的总数是 & 16 的,行列地址线在实际的使用过程中是分时复用的。
二、内存大小的修改
修改用来显示的内存大小
./include/configs/smdk6410.h:#define PHYS_SDRAM_1_SIZE
./include/configs/smdk6410.h:#define PHYS_SDRAM_1_SIZE
0x /* 256 MB */
修改CPU中对应的寄存器配置
《s3c6410_rev12.pdf》196页,5.5.4 MEMORY CONFIGURATION REGISTER
0x7E00100C
32-bit DRAM controller memory config register
0x7E00100C
32-bit DRAM controller memory config register
Active chips
00 = 1 chip
01 = 2 chips
10 = Reserved
11 = Reserved
Active chips
00 = 1 chip
01 = 2 chips
10 = Reserved
11 = Reserved
这里我们是2片所以要选成 01
Row bits [5:3]
000 = 11 bits
001 = 12 bits
010 = 13 bits
011 = 14 bits
100 = 15 bits
101 = 16 bits
Row bits [5:3]
000 = 11 bits
001 = 12 bits
010 = 13 bits
011 = 14 bits
100 = 15 bits
101 = 16 bits
Column bits
000 = 8 bits
001 = 9 bits
010 = 10 bits
011 = 11 bits
100 = 12 bits
Column bits
000 = 8 bits
001 = 9 bits
010 = 10 bits
011 = 11 bits
100 = 12 bits
以256M内存芯片的参数为例
《K4X1G163PC.pdf》芯片手册中描述如下:
32M x16 BA0,BA1 A0 - A13 A0 - A9
32M x16 BA0,BA1 A0 - A13 A0 - A9
这里我们需要将Row设置为011将Column设置为010
uboot代码中我们可以看到
./include/s3c6410.h:#define ELFIN_DMC1_BASE
0x7e001000
./include/s3c6410.h:#define ELFIN_DMC1_BASE
0x7e001000
./include/s3c6410.h:#define INDEX_DMC_MEMORY_CFG
./include/s3c6410.h:#define INDEX_DMC_MEMORY_CFG
./include/s3c6410.h:#define DMC1_MEM_CFG
./include/s3c6410.h:#define DMC1_MEM_CFG
实际上,如果通过读CPU的手册而将内存配置正确,是件挺困的事情,还好我们有很多可参考的代码,但我们需要弄清楚这些代码为什么要这样修改
修改行列地址线的配置
#define DMC1_MEM_CFG 0x0001001A
#define DMC1_MEM_CFG 0x0001001A
1A转换为二进制为,可以看到,是我们查到的Row,Column的寄存器配置的值
在有些代码中也修改了 #define DMC1_CHIP0_CFG
0x150F8 这个寄存器的值,为 #define DMC1_CHIP0_CFG
翻遍了资料也不知道这个值是做什么用的,为什么要修改,所以我这里并没有修改这个值,也希望有经验的朋友能告诉我为什么。
修改UBOOT的自举地址
一般考虑修改UBOOT自举地址都会去修改 board/samsum/smdk6410/config.mk 中的 TEXT_BASE 的值,但这里不能做
网上下来资料看了下,目前大多数的s3c6410开发板都没有去修改UBOOT的自举地址,这里记录了下查找及修改的过程
cpu/s3c64xx/start.S 中看到
#ifdef CONFIG_BOOT_MOVINAND
sp, _TEXT_PHY_BASE
movi_bl2_copy
after_copy
cpu/s3c64xx/start.S 中看到
#ifdef CONFIG_BOOT_MOVINAND
sp, _TEXT_PHY_BASE
movi_bl2_copy
after_copy
_TEXT_PHY_BASE:
CFG_PHY_UBOOT_BASE
_TEXT_PHY_BASE:
CFG_PHY_UBOOT_BASE
修改 include/configs/smdk6410.h
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0x7e00000
/* 128 MB in DRAM
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0xfe00000
/* 256 MB in DRAM
#ifdef CONFIG_ENABLE_MMU
#define CFG_UBOOT_BASE 0xcfe00000
#define CFG_UBOOT_BASE 0x5fe00000
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0XFE00000
修改 include/configs/smdk6410.h
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0x7e00000
/* 128 MB in DRAM
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0xfe00000
/* 256 MB in DRAM
#ifdef CONFIG_ENABLE_MMU
// #define CFG_UBOOT_BASE
0xc7e00000
#define CFG_UBOOT_BASE 0xcfe00000
//#define CFG_UBOOT_BASE
0x57e00000
#define CFG_UBOOT_BASE 0x5fe00000
//#define CFG_PHY_UBOOT_BASE
MEMORY_BASE_ADDRESS + 0x7e00000
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0XFE00000
修改 board/samsung/smdk6410/config.mk
TEXT_BASE = 0xcfe00000
修改 board/samsung/smdk6410/config.mk
//TEXT_BASE = 0xc7e00000
TEXT_BASE = 0xcfe00000
修改 include/movi.h
#define BL2_BASE
0x5FE00000
这宏只作用于movi_bl2_copy函数,因为我们需要将UBOOT自举到256的最顶端
修改 include/movi.h
//#define BL2_BASE
0x57E00000
#define BL2_BASE
0x5FE00000
这宏只作用于movi_bl2_copy函数,因为我们需要将UBOOT自举到256的最顶端
修改MMU地址映射规则
修改 board/samsung/smdk6410/lowlevel_init.S
找到 mmu_table:将
.set __base, 0x500
.rept 0xC80 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.rept 0x1000 - 0xc80
.set __base, 0x500
.rept 0xd00 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.rept 0x1000 - 0xd00
修改 board/samsung/smdk6410/lowlevel_init.S
找到 mmu_table:将
// 128MB for SDRAM 0xC0000000 -& 0x
.set __base, 0x500
.rept 0xC80 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
// access is not allowed.
.rept 0x1000 - 0xc80
// 256MB for SDRAM 0xC0000000 -& 0x
.set __base, 0x500
.rept 0xd00 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
// access is not allowed.
.rept 0x1000 - 0xd00
UBOOT启动起来,敲入 md cfe00000,打印出内存的数据,可以查看到跟我们编译出来的u-boot.bin是一致的
三、参考资料
专家详解:内存工作原理及发展历程(值得一看)
6410平台上配置Linux的DDR参数(直接给出了结果,可以有个参考)
S3C6410的DRAM控制器(罗列的非常清楚)
《s3c6410_rev12.pdf》
《OK6410开发板硬件手册2.1.pdf》
《K4X51163PC.pdf》
《K4X1G163PC.pdf》
前章我们已经可以制作出用来启动的SD卡了,并将自己编译的UBOOT烧录到了SD卡中
这份UBOOT代码中的内存配置肯定和手头的开发板不一致,这里我们将搞清楚如何修改内存大小,并说清楚内存大小到底是如何计算的
一、关于内存大小的计算
1)硬件的型号,在飞凌的开发板中
128M内存 采用的是 K4X51163PC 可以看出来 这颗芯片是 总大小为512 bit 位宽 为16
256M内存 采用的是 K4X1G163PC 可以看出来 这颗芯片是 总大小为1G
bit 位宽 为16
2)贴片数量2,都是用2DDR,来构成32位
3)查看DDR的芯片手册
128M内存 总共有 4 个 BANK 行列地址线数量分别为 Row = 13 Col = 10
256M内存 总共有 4 个 BANK 行列地址线数量分别为 Row = 14 Col = 10
4)以256M内存为例,内存大小的计算为
每个BANK的存储单元个数 = (2^Row)*(2^Col) = 2^(Row+Col)
每个存储单元 可以存放 16 位 的数据
总容量 = 芯片个数 * 每个芯片的BANK数量 * 位宽 * 行地址线数量 * 列地址线数量 (这里的单位都是bit)
= 2*4*16*(2^14)*(2^10) = 2^(1+2+4+14+10)= 2^31 bit
转换为 字节 2^28
转换为兆 2^8 M = 256 M
这里需要说明的是 行列地址线的总数是 & 16 的,行列地址线在实际的使用过程中是分时复用的。
二、内存大小的修改
修改用来显示的内存大小
./include/configs/smdk6410.h:#define PHYS_SDRAM_1_SIZE
./include/configs/smdk6410.h:#define PHYS_SDRAM_1_SIZE
0x /* 256 MB */
修改CPU中对应的寄存器配置
《s3c6410_rev12.pdf》196页,5.5.4 MEMORY CONFIGURATION REGISTER
0x7E00100C
32-bit DRAM controller memory config register
0x7E00100C
32-bit DRAM controller memory config register
Active chips
00 = 1 chip
01 = 2 chips
10 = Reserved
11 = Reserved
Active chips
00 = 1 chip
01 = 2 chips
10 = Reserved
11 = Reserved
这里我们是2片所以要选成 01
Row bits [5:3]
000 = 11 bits
001 = 12 bits
010 = 13 bits
011 = 14 bits
100 = 15 bits
101 = 16 bits
Row bits [5:3]
000 = 11 bits
001 = 12 bits
010 = 13 bits
011 = 14 bits
100 = 15 bits
101 = 16 bits
Column bits
000 = 8 bits
001 = 9 bits
010 = 10 bits
011 = 11 bits
100 = 12 bits
Column bits
000 = 8 bits
001 = 9 bits
010 = 10 bits
011 = 11 bits
100 = 12 bits
以256M内存芯片的参数为例
《K4X1G163PC.pdf》芯片手册中描述如下:
32M x16 BA0,BA1 A0 - A13 A0 - A9
32M x16 BA0,BA1 A0 - A13 A0 - A9
这里我们需要将Row设置为011将Column设置为010
uboot代码中我们可以看到
./include/s3c6410.h:#define ELFIN_DMC1_BASE
0x7e001000
./include/s3c6410.h:#define ELFIN_DMC1_BASE
0x7e001000
./include/s3c6410.h:#define INDEX_DMC_MEMORY_CFG
./include/s3c6410.h:#define INDEX_DMC_MEMORY_CFG
./include/s3c6410.h:#define DMC1_MEM_CFG
./include/s3c6410.h:#define DMC1_MEM_CFG
实际上,如果通过读CPU的手册而将内存配置正确,是件挺困的事情,还好我们有很多可参考的代码,但我们需要弄清楚这些代码为什么要这样修改
修改行列地址线的配置
#define DMC1_MEM_CFG 0x0001001A
#define DMC1_MEM_CFG 0x0001001A
1A转换为二进制为,可以看到,是我们查到的Row,Column的寄存器配置的值
在有些代码中也修改了 #define DMC1_CHIP0_CFG
0x150F8 这个寄存器的值,为 #define DMC1_CHIP0_CFG
翻遍了资料也不知道这个值是做什么用的,为什么要修改,所以我这里并没有修改这个值,也希望有经验的朋友能告诉我为什么。
修改UBOOT的自举地址
一般考虑修改UBOOT自举地址都会去修改 board/samsum/smdk6410/config.mk 中的 TEXT_BASE 的值,但这里不能做
网上下来资料看了下,目前大多数的s3c6410开发板都没有去修改UBOOT的自举地址,这里记录了下查找及修改的过程
cpu/s3c64xx/start.S 中看到
#ifdef CONFIG_BOOT_MOVINAND
sp, _TEXT_PHY_BASE
movi_bl2_copy
after_copy
cpu/s3c64xx/start.S 中看到
#ifdef CONFIG_BOOT_MOVINAND
sp, _TEXT_PHY_BASE
movi_bl2_copy
after_copy
_TEXT_PHY_BASE:
CFG_PHY_UBOOT_BASE
_TEXT_PHY_BASE:
CFG_PHY_UBOOT_BASE
修改 include/configs/smdk6410.h
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0x7e00000
/* 128 MB in DRAM
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0xfe00000
/* 256 MB in DRAM
#ifdef CONFIG_ENABLE_MMU
#define CFG_UBOOT_BASE 0xcfe00000
#define CFG_UBOOT_BASE 0x5fe00000
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0XFE00000
修改 include/configs/smdk6410.h
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0x7e00000
/* 128 MB in DRAM
#define CFG_MEMTEST_END
MEMORY_BASE_ADDRESS + 0xfe00000
/* 256 MB in DRAM
#ifdef CONFIG_ENABLE_MMU
// #define CFG_UBOOT_BASE
0xc7e00000
#define CFG_UBOOT_BASE 0xcfe00000
//#define CFG_UBOOT_BASE
0x57e00000
#define CFG_UBOOT_BASE 0x5fe00000
//#define CFG_PHY_UBOOT_BASE
MEMORY_BASE_ADDRESS + 0x7e00000
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0XFE00000
修改 board/samsung/smdk6410/config.mk
TEXT_BASE = 0xcfe00000
修改 board/samsung/smdk6410/config.mk
//TEXT_BASE = 0xc7e00000
TEXT_BASE = 0xcfe00000
修改 include/movi.h
#define BL2_BASE
0x5FE00000
这宏只作用于movi_bl2_copy函数,因为我们需要将UBOOT自举到256的最顶端
修改 include/movi.h
//#define BL2_BASE
0x57E00000
#define BL2_BASE
0x5FE00000
这宏只作用于movi_bl2_copy函数,因为我们需要将UBOOT自举到256的最顶端
修改MMU地址映射规则
修改 board/samsung/smdk6410/lowlevel_init.S
找到 mmu_table:将
.set __base, 0x500
.rept 0xC80 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.rept 0x1000 - 0xc80
.set __base, 0x500
.rept 0xd00 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.rept 0x1000 - 0xd00
修改 board/samsung/smdk6410/lowlevel_init.S
找到 mmu_table:将
// 128MB for SDRAM 0xC0000000 -& 0x
.set __base, 0x500
.rept 0xC80 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
// access is not allowed.
.rept 0x1000 - 0xc80
// 256MB for SDRAM 0xC0000000 -& 0x
.set __base, 0x500
.rept 0xd00 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
// access is not allowed.
.rept 0x1000 - 0xd00
UBOOT启动起来,敲入 md cfe00000,打印出内存的数据,可以查看到跟我们编译出来的u-boot.bin是一致的
三、参考资料
专家详解:内存工作原理及发展历程(值得一看)
6410平台上配置Linux的DDR参数(直接给出了结果,可以有个参考)
S3C6410的DRAM控制器(罗列的非常清楚)
《s3c6410_rev12.pdf》
《OK6410开发板硬件手册2.1.pdf》
《K4X51163PC.pdf》
《K4X1G163PC.pdf》
&&&&推荐文章:
【上篇】【下篇】1310人阅读
下面结合移植uboot到 s3c2440来分析如何改写相关的uboot源码(上节已经给出uboot源码,可以参考)&
根据cpu/arm920t/u-boot.lds中指定的连接方式:
&&&& 看一下uboot.lds文件,在board/smdk2410目录下面,uboot.lds是告诉编译器这些段改怎么划分,GUN编译过的段,最基本的 三个段是RO,RW,ZI,RO表示只读,对应于具体的指代码段,RW是数据段,ZI是归零段,就是全局变量的那段。Uboot代码这么多,如何保证 start.s会第一个执行,编译在最开始呢?就是通过uboot.lds链接文件进行
OUTPUT_FORMAT(&elf32-littlearm&, &elf32-littlearm&, &elf32-littlearm&)
/*OUTPUT_FORMAT(&elf32-arm&, &elf32-arm&, &elf32-arm&)*/
OUTPUT_ARCH(arm)
ENTRY(_start)
. = 0x; //起始地址
. = ALIGN(4); //4字节对齐
.text : //test指代码段,上面3行标识是不占用任何空间的
cpu/arm920t/start.o (.text) //这里把start.o放在第一位就表示把start.s编
译时放到最开始,这就是为什么把uboot烧到起始地址上它肯定运行的是start.s
. = ALIGN(4); //前面的 “.” 代表当前值,是计算一个当前的值,是计算上
面占用的整个空间,再加一个单元就表示它现在的位置
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .; //bss表示归零段
.bss : { *(.bss) }
&&&&&& 第一个链接的是cpu/arm920t/start.o,因此u-boot.bin的入口代码在cpu/arm920t/start.o中,其源代码在 cpu/arm920t/start.S中。下面我们来分析cpu/arm920t/start.S的执行。
1.&&&&& 硬件设备初始化
(1)设置异常向量
&&&&&&&&& 下面代码是系统启动后U-boot上电后运行的第一段代码,它是什么意思?
&&&&&&&&& u-boot对应的第一阶段代码放在cpu/arm920t/start.S文件中,入口代码如下:.
globl _startglobal&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& /*声明一个符号可被其它文件引用,相当于声明了一个全局变量,.globl与.global相同*/
_start:&&& b&&&& start_code&&&&&&&&&&&&&&&&&&& /* 复位 */b是不带返回的跳转(bl是带返回的跳转),意思是无条件直接跳转到start_code标号出执行程序
&&&&&& ldr&& pc, _undefined_instruction&&&&& /*&未定义指令向量 l---dr相当于mov操作*/
&&&&&& ldr&& pc, _software_interrupt&&&&&&&&&&& /* &软件中断向量 */
&&&&&& ldr&& pc, _prefetch_abort&&&&&&&&&&&&&&&&& /*& 预取指令异常向量 */
&&&&&& ldr&& pc, _data_abort&&&&&&&&&&&&&&&&&&&&&&& /* &数据操作异常向量 */
&&&&&& ldr&& pc, _not_used&&&&&&&&&&&&&&&&&&&&&&&&&& /*& 未使用&& */
&&&&&& ldr&& pc, _irq&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& /*& irq中断向量& */
&&&&&& ldr&& pc, _fiq&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& /*& fiq中断向量& */
/*& 中断向量表入口地址 */
_undefined_instruction:&&& .word undefined_instruction& /*就是在当前地址,即_undefined_instruction 处存放 undefined_instruction*/
_software_interrupt:& .word software_interrupt
_prefetch_abort:& .word prefetch_abort
_data_abort:&&&&&&& .word data_abort
_not_used:&&&&&&&&& .word not_used
_irq:&&&&&&&&&&&&&&&&&&&& .word irq
_fiq:&&&&&&&&&&&&&&&&&&&& .word fiq&
word伪操作用于分配一段字内存单元(分配的单元都是字对齐的),并用伪操作中的expr初始化
&&&&&& .balignl 16,0xdeadbeef
&&&&&& 他们是系统定义的异常,一上电程序跳转到_start异常处执行相应的汇编指令,下面定义出的都是不同的异常,比如软件发生软中断时,CPU就会去执行软中断的指令,这些异常中断在CPU中地址是从0开始,每个异常占4个字节
&&&&&&&ldr pc, _undefined_instruction表示把_undefined_instruction存放的数值存放到pc指针上
&&&&&&&&&&&&&&&&& &_undefined_instruction: .word undefined_instruction表示未定义的这个异常是由.word来定义的,它表示定义一个字,一个32位的数
.& word后面的数:表示把该标识的编译地址写入当前地址,标识是不占用任何指令的。把标识存放的数值copy到指针pc上面,那么标识上存放的值是什么?
是由.word undefined_instruction来指定的,pc就代表你运行代码的地址,她就实现了CPU要做一次跳转时的工作。
&&&&&&&以上代码设置了ARM异常向量表,各个异常向量介绍如下:
表 2.1 ARM异常向量表
&&&&&&&地址&
&&& 进入模式
&& 管理模式
&&& 复位电平有效时,产生复位异常,程序跳转到复位处理程序处执行
未定义指令
&&&未定义模式
&& 遇到不能处理的指令时,产生未定义指令异常
&& 管理模式
&&& 执行SWI指令产生,用于用户模式下的程序调用特权操作指令
0x0000000c
&& 中止模式
&& 处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常
&& 中止模式
&& 处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常
&&& 外部中断请求有效,且CPSR中的I位为0时,产生IRQ异常
0x0000001c
&&& 快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常
&&&&& 在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。
&&&&&& 其中复位异常向量的指令“b restart”决定了U-Boot启动后将自动跳转到标号“restart”处执行。
(2)CPU进入SVC模式
start_code:
&&&&&& &* set the cpu to SVC32 mode
&&&&&& &*/
&&&&&& mrs r0, cpsr
&&&&&& bic& r0, r0, #0x1f&&&&&&& /*工作模式位清零 */
&&&&&& orr&& r0, r0, #0xd3&&&&&&&&&&&&& /*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1 */
&&&&&& msr cpsr, r0
&&&&&& 以上代码将CPU的工作模式位设置为管理模式,即设置相应的CPSR程序状态字,并将中断禁止位和快中断禁止位置一,从而屏蔽了IRQ和FIQ中断。
&&&&&& 操作系统先注册一个总的中断,然后去查是由哪个中断源产生的中断,再去查用户注册的中断表,查出来后就去执行用户定义的用户中断处理函数。
(3)设置控制寄存器地址
# if defined(CONFIG_S3C2400)&&&&&&& /*关闭看门狗*/
#& define pWTCON 0x&&&&&& /*;看门狗寄存器*/
#& define INTMSK& 0x&&&&&&& /*;中断屏蔽寄存器*/
#& define CLKDIVN&&&&& 0x /*;时钟分频寄存器*/
#else&&&&& /* s3c2410与s3c2440下面4个寄存器地址相同 */
#& define pWTCON 0x&&&&&&&&&&&&&& /* WATCHDOG控制寄存器地址 */
#& define INTMSK& 0x4A000008&&&&&&&&&&&&&&&&&&&& /* INTMSK寄存器地址& */
#& define INTSUBMSK 0x4A00001C&&&&& /* INTSUBMSK寄存器地址 次级中断屏蔽寄存器*/
#& define CLKDIVN&&&&& 0x4C000014&&&&&& & &&&&&&&&& /* CLKDIVN寄存器地址&;时钟分频寄存器*/
&&&&&& 对与s3c2440开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四个寄存器的地址的设置。
(4)关闭看门狗
&&&&&& ldr&& r0, =pWTCON&& /*将pwtcon寄存器地址赋给R0*/
&&&&&& mov&&&&&& r1, #0x0&&&&& /*r1的内容为0*/
&&&&&& str&& r1, [r0]&&&&&&&&&&&&&& &/* 看门狗控制器的最低位为0时,看门狗不输出复位信号 */
&&&&&& 以上代码向看门狗控制寄存器写入0,关闭看门狗。否则在U-Boot启动过程中,CPU将不断重启。
为什么要关看门狗?
&&&&&&& &就是防止,不同得两个以上得CPU,进行喂狗的时间间隔问题:说白了,就是你运行的代码如果超出喂狗时间,而你不关狗,就会导致,你代码还没运行完又得去喂狗,就这样反复得重启CPU,那你代码永远也运行不完,所以,得先关看门狗得原因,就是这样。
关狗---详细的原因:
&&&&& 关闭看门狗,关闭中断,所谓的喂狗是每隔一段时间给某个寄存器置位而已,在实际中会专门启动一个线程或进程会专门喂狗,当上层软件出现故障时就会停止喂狗,
&&&&& 停止喂狗之后,cpu会自动复位,一般都在外部专门有一个看门狗,做一个外部的电路,不在cpu内部使用看门狗,cpu内部的看门狗是复位的cpu
&&&&&& 当开发板很复杂时,有好几个cpu时,就不能完全让板子复位,但我们通常都让整个板子复位。看门狗每隔短时间就会喂狗,问题是在两次喂狗之间的时间间隔 内,运行的代码的时间是否够用,两次喂狗之间的代码是否在两次喂狗的时间延迟之内,如果在延迟之外的话,代码还没改完就又进行喂狗,代码永远也改不完
(5)屏蔽中断
&&&&&& &* mask all IRQs by setting all bits in the INTMR - default
&&&&&& &*/
&&&&&& mov&&&&&& r1, #0xffffffff&&&& /*屏蔽所有中断,&某位被置1则对应的中断被屏蔽 */&/*寄存器中的值*/
&&&&&& ldr&& r0, =INTMSK&&&&&& /*将管理中断的寄存器地址赋给ro*/
&&&&&& str&& r1, [r0]&&&&&&&&&&&&&&&&&&/*将全r1的值赋给ro地址中的内容*/
&&&&&&      &INTMSK是主中断屏蔽寄存器,每一位对应SRCPND(中断源引脚寄存器)中的一位,表明SRCPND相应位代表的中断请求是否被CPU所处理。
& & &       &INTMSK寄存器是一个32位的寄存器,每位对应一个中断,向其中写入0xffffffff就将INTMSK寄存器全部位置一,从而屏蔽对应的中断。
# if defined(CONFIG_S3C2440)
&&& &&&&& ldr& r1, =0x7fff&&&&&&&&&&&&&&&&&&
& &&&&&& ldr& r0, =INTSUBMSK&&
&&&&&&&& str& r1, [r0]&&&&&&&&&&&&
&&&&&& INTSUBMSK每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。
& & & & &     INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低15位。向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)置一,从而屏蔽对应的中断。
屏蔽所有中断,为什么要关中断?
    中断处理中ldr pc是将代码的编译地址放在了指针上,而这段时间还没有搬移代码,所以编译地址上面没有这个代码,如果进行跳转就会跳转到空指针上面
(6)设置MPLLCON,UPLLCON, CLKDIVN
# if defined(CONFIG_S3C2440)&
#define MPLLCON&& 0x4C000004
#define UPLLCON&& 0x4C000008&&
&&& &&&&& ldr& r0, =CLKDIVN&&&;设置时钟
&&& &&&&& mov& r1, #5
&&& &&&&& str& r1, [r0]
&&& &&&&& ldr& r0, =MPLLCON
&&& &&&&& ldr& r1, =0x7F021&
&&& &&&&& str& r1, [r0]
&& &ldr& r0, =UPLLCON&
&&& &&&&& ldr& r1, =0x38022
&&& &&&&& str& r1, [r0]
&&&&&& /* FCLK:HCLK:PCLK = 1:2:4 */
&&&&&& /* default FCLK is 120 MHz ! */
&&&&&& ldr&& r0, =CLKDIVN
&&&&&& mov&&&&&& r1, #3
&&&&&& str&& r1, [r0]
&&&&&& CPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。这就需要设置CLKDIVN,MPLLCON,UPLLCON这3个寄存器。
&&&&&&&CLKDIVN寄存器用于设置FCLK,HCLK,PCLK三者间的比例,可以根据表2.2来设置。
表 2.2 S3C2440 的CLKDIVN寄存器格式
&&&&&&&&&&&&&&&&&&&&&&& && CLKDIVN&&&&&&&&&&&&& &&&&&&&&&&
&&&&&&&&&&&&&& & 位&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&& &初始值&&&&&&&&&&&&&&&&&&&&
&&&&& 00 : HCLK = FCLK/1.
&&&&&&01 : HCLK = FCLK/2.
&&&&& 10 : HCLK = FCLK/4 (当 CAMDIVN[9] = 0 时)
&&&&& HCLK= FCLK/8& (当 CAMDIVN[9] = 1 时)
&&&&& 11 : HCLK = FCLK/3 (当 CAMDIVN[8] = 0 时)
&&&&& HCLK = FCLK/6 (当 CAMDIVN[8] = 1时)
0: PCLK = HCLK/1&& 1: PCLK = HCLK/2
&&&&&& 设置CLKDIVN为5,就将HDIVN设置为二进制的10,由于CAMDIVN[9]没有被改变过,取默认值0,因此HCLK = FCLK/4。PDIVN被设置为1,因此PCLK= HCLK/2。因此分频比FCLK:HCLK:PCLK = 1:4:8 。
&&&&&& MPLLCON寄存器用于设置FCLK与Fin的倍数。MPLLCON的位[19:12]称为MDIV,位[9:4]称为PDIV,位[1:0]称为SDIV。
&&&&&& 对于S3C2440,FCLK与Fin的关系如下面公式:
&&&&&& MPLL(FCLK) = (2×m×Fin)/(p× )
&&&&&& 其中: m=MDIC+8,p=PDIV+2,s=SDIV
&&&&&& MPLLCON与UPLLCON的值可以根据参考文献4中“PLL VALUE SELECTION TABLE”设置。该表部分摘录如下:
表 2.3 推荐PLL值
&&&&&& 输入频率&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&& 输出频率&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&& MDIV&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&& PDIV&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&SDIV&&&&&&&&&&&&&&&&&&&&&
12.0000MHz
12.0000MHz
405.00 MHz
&&&&&& 当tq2440系统主频设置为405MHZ,USB时钟频率设置为48MHZ时,系统可以稳定运行,因此设置MPLLCON与UPLLCON为:
&&&&&& MPLLCON=(0x7f&&12) | (0x02&&4) | (0x01) = 0x7f021
&&&&&& UPLLCON=(0x38&&12) | (0x02&&4) | (0x02) = 0x38022
默认频率为&&&&& FCLK:HCLK:PCLK = 1:2:4,默认 FCLK 的值为 120 MHz,该值为 S3C2410 手册的推荐值。
设置时钟分频,为什么要设置时钟?
起始可以不设,系统能不能跑起来和频率没有任何关系,频率的设置是要让外围的设备能承受所设置的频率,如果频率过高则会导致cpu操作外围设备失败
说白了:设置频率,就为了CPU能去操作外围设备
(7)关闭MMU,cache& ------(也就是做bank的设置)
&&&&&& 接着往下看:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
&&&&&& bl&&& cpu_init_crit&&/*&;跳转并把转移后面紧接的一条指令地址保存到链接寄存器LR(R14)中,以此来完成子程序的调用*/
&&&&&& cpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。
&&&&&& 下面分析一下cpu_init_crit到底做了什么:
320& #ifndef CONFIG_SKIP_LOWLEVEL_INIT
321& cpu_init_crit:
322& &&& /*
323& &&& &* 使数据cache与指令cache无效 */
324& &&& &*/&
325& &&& mov&&&&&& r0, #0
326& &&& mcr p15, 0, r0, c7, c7, 0&&& /* 向c7写入0将使ICache与DCache无效*/
327& &&& mcr p15, 0, r0, c8, c7, 0&&& /* 向c8写入0将使TLB失效 ,协处理器*/&&
329& &&& /*
330& &&& &* disable MMU stuff and caches
331& &&& &*/
332& &&& mrc p15, 0, r0, c1, c0, 0&&& /*& 读出控制寄存器到r0中& */
333& &&& bic& r0, r0, #0x&& @ clear bits 13, 9:8 (--V- --RS)
334& &&& bic& r0, r0, #0x&& @ clear bits 7, 2:0 (B--- -CAM)
335& &&& orr&& r0, r0, #0x&& @ set bit 2 (A) Align
336& &&& orr&& r0, r0, #0x&& @ set bit 12 (I) I-Cache
337& &&& mcr p15, 0, r0, c1, c0, 0&&& /*& 保存r0到控制寄存器& */
339& &&& /*
340& &&& &* before relocating, we have to setup RAM timing
341& &&& &* because memory timing is board-dependend, you will
342& &&& &* find a lowlevel_init.S in your board directory.
343& &&& &*/
344& &&& mov&&&&&& ip, lr
346& &&& bl&&& lowlevel_init
348& &&& mov&&&&&& lr, ip
349& &&& mov&&&&&& pc, lr
350& #endif /* CONFIG_SKIP_LOWLEVEL_INIT */
&&&&&&&&&&&&&& 代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。其中c7是cache控制寄存器,c8是TLB控制寄存器。325~327行代码将0写入c7、c8,使Cache,TLB内容无效。
&&&&&& 第332~337行代码关闭了MMU。这是通过修改CP15的c1寄存器来实现的,先看CP15的c1寄存器的格式(仅列出代码中用到的位):
表 2.3 CP15的c1寄存器格式(部分)
&&&&& 15&&&&&&&
&&&&&& 14&&&&&&
&& 13&&&&&
&&&&&& 12&&&&&&
&&&& 11&&&&&&
&&& 10&&&&&
&&&&&& 9&&&&&&
&&&&&&& 8&&&&&&
&&&&&& 7&&&&&&
&&&& 6&&&&&&&
&&&&&& 5&&&&&&
&&&&& 4&&&&&
&&&&&& 3&&&&&
&&&&&&&& 2&&&&&
&&&& 1&&&&
&&&&&&& 0&&&&&&&&
&&&&&& 各个位的意义如下:
V : &表示异常向量表所在的位置,0:异常向量在0x:异常向量在 0xFFFF0000
I : &0 :关闭ICaches;1 :开启ICaches
R、S : 用来与页表中的描述符一起确定内存的访问权限
B : &0 :CPU为小字节序;1 : CPU为大字节序
C : &0:关闭DCaches;1:开启DCaches
A : &0:数据访问时不进行地址对齐检查;1:数据访问时进行地址对齐检查
M : &0:关闭MMU;1:开启MMU
&&&&&& 332~337行代码将c1的 M位置零,关闭了MMU。
为什么要关闭catch和MMU呢?catch和MMU是做什么用的?
&&&&MMU是Memory Management Unit的缩写,中文名是单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路
&&& 同时也负责映射为物理地址,以及提供硬件机制的内存访问授权&&&&&&
一,关catch
&&&&&& catch和MMU是通过CP15管理的,刚上电的时候,CPU还不能管理他们
&&&&&& 上电的时候MMU必须关闭,指令catch可关闭,可不关闭,但数据catch一定要关闭
&&&&&& 否则可能导致刚开始的代码里面,去取数据的时候,从catch里面取,而这时候RAM中数据还没有catch过来,导致数据预取异常
&&&&& 因为MMU是;把虚拟地址转化为物理地址得作用
&&&&& 而目的是设置控制寄存器,而控制寄存器本来就是实地址(物理地址),再使能MMU,不就是多此一举了吗?
详细分析---
&&&&& Catch是cpu内部的一个2级缓存,它的作用是将常用的数据和指令放在cpu内部,MMU是用来把虚实地址转换为物理地址用的
&&&&& 我们的目的:是设置控制的寄存器,寄存器都是实地址(物理地址),如果既要开启MMU又要做虚实地址转换的话,中间还多一步,多此一举了嘛?
&&&&& 先要把实地址转换成虚地址,然后再做设置,但对uboot而言就是起到一个简单的初始化的作用和引导操作系统,如果开启MMU的话,很麻烦,也没必要,所以关闭MMU.
&&&&& &说到catch就必须提到一个关键字Volatile,以后在设置寄存器时会经常遇到,他的本质:是告诉编译器不要对我的代码进行优化,作用是让编写者感觉不倒变量的变化情况(也就是说,让它执行速度加快吧)
&&&&&&&优化的过程:是将常用的代码取出来放到catch中,它没有从实际的物理地址去取,它直接从cpu的缓存中去取,但常用的代码就是为了感觉一些常用变量的变化
&&&&&&&&优化原因:如果正在取数据的时候发生跳变,那么就感觉不到变量的变化了,所以在这种情况下要用Volatile关键字告诉编译器不要做优化,每次从实际的物理地址中去取指令,这就是为什么关闭catch关闭MMU。
&&&&&&& &但在C语言中是不会关闭catch和MMU的,会打开,如果编写者要感觉外界变化,或变化太快,从catch中取数据会有误差,就加一个关键字Volatile。
(8)初始化RAM控制寄存器
&&&&&&&&&&&&&&&&&&&& bl lowlevel_init下来初始化各个bank,把各个bank设置必须搞清楚,对以后移植复杂的uboot有很大帮助
&&&&&&&&&&&&&&&&&&&& 设置完毕后拷贝uboot代码到4k空间,拷贝完毕后执行内存中的uboot代码
&&&&& 其中的lowlevel_init就完成了内存初始化的工作,由于内存初始化是依赖于开发板的,因此lowlevel_init的代码一般放在board 下面相应的目录中。lowlevel_init在board/smdk2410/lowlevel_init.S中定义如下:
45& #define BWSCON&& 0x&&&&&&& /* 13个存储控制器的开始地址 */
129& _TEXT_BASE:
130& &&& .word&&&& TEXT_BASE&&&&&&&0x33F80000, board/config.mk中这段话表示,用户告诉编译器编译地址的起始地址
132& .globl lowlevel_init
133& lowlevel_init:
134& &&& /* memory control configuration */
135& &&& /* make r0 relative the current location so that it */
136& &&& /* reads SMRDATA out of FLASH rather than memory ! */
137& &&& ldr&&&& r0, =SMRDATA
138& &&& ldr&& r1, _TEXT_BASE
139& &&& sub& r0, r0, r1&&&&&&&&&&&&& /* SMRDATA减 _TEXT_BASE就是13个寄存器的偏移地址 */
140& &&& ldr&& r1, =BWSCON&& /* Bus Width Status Controller */
141& &&& add&&&& r2, r0, #13*4
143& &&& ldr&&&& r3, [r0], #4&&& /*将13个寄存器的值逐一赋值给对应的寄存器*/
144& &&& str&&&& r3, [r1], #4
145& &&& cmp&&&& r2, r0
146& &&& bne& &&&0b
148& &&& /* everything is fine now */
149& &&& mov&&&&&& pc, lr
151& &&& .ltorg
152& /* the literal pools origin */
154& SMRDATA:&&&&&&&&&&& /*& 下面是13个寄存器的值& */
155& .word& … …
156 & .word& … …
&&&&&& lowlevel_init初始化了13个寄存器来实现RAM时钟的初始化。lowlevel_init函数对于U-Boot从NAND Flash或NOR Flash启动的情况都是有效的。
&&&&&& U-Boot.lds链接脚本有如下代码:
&&&&&& .text :
&&&&&&&&&&&&&&&&&&&& cpu/arm920t/start.o&&& (.text)
&&&& &&&&&&&&&& board/samsung/mini2440/lowlevel_init.o (.text)
&&& &&&&&&&&&&&& board/samsung/mini2440/nand_read.o (.text)
&&&&&&&&&&&&& … …
&&&&&& board/samsung/mini2440/lowlevel_init.o将被链接到cpu/arm920t/start.o后面,因此board /samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代码中。
&&&&&& U-Boot在NAND Flash启动时,lowlevel_init.o将自动被读取到CPU内部4KB的内部RAM中。因此第137~146行的代码将从CPU内部RAM中复制寄存器的值到相应的寄存器中。
&&&&&& 对于U-Boot在NOR Flash启动的情况,由于U-Boot连接时确定的地址是U-Boot在内存中的地址,而此时U-Boot还在NOR Flash中,因此还需要在NOR Flash中读取数据到RAM中。
&&&&&& 由于NOR Flash的开始地址是0,而U-Boot的加载到内存的起始地址是TEXT_BASE,SMRDATA标号在Flash的地址就是SMRDATA-TEXT_BASE。
&&&&&& 综上所述,lowlevel_init的作用就是将SMRDATA开始的13个值复制给开始地址[BWSCON]的13个寄存器,从而完成了存储控制器的设置。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
&问题一:如果换一块开发板有可能改哪些东西?
&&&&&&&&&&&&&&&&& &首先,cpu的运行模式,如果需要对cpu进行设置那就设置,管看门狗,关中断不用改,时钟有可能要改,如果能正常使用则不用改,关闭catch和 MMU不用改,设置bank有可能要改。最后一步拷贝时看地址会不会变,如果变化也要改,执行内存中代码,地址有可能要改。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
问题二:Nor Flash和Nand Flash本质区别:
&&&&&&&&&&&&&&&&& 就在于是否进行代码拷贝,也就是下面代码所表述:无论是Nor Flash还是Nand Flash,核心思想就是将uboot代码搬运到内存中去运行,但是没有拷贝bss后面这段代码,只拷贝bss前面的代码,bss代码是放置全局变量的。 Bss段代码是为了清零,拷贝过去再清零重复操作
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(9)复制U-Boot第二阶段代码到RAM
&&&&&& cpu/arm920t/start.S原来的代码是只支持从NOR Flash启动的,经过修改现在U-Boot在NOR Flash和NAND Flash上都能启动了,实现的思路是这样的:
&&&&&& bl&&& bBootFrmNORFlash /*& 判断U-Boot是在NAND Flash还是NOR Flash启动& */
&&&&&& cmp&&&&&& r0, #0&&&&&&&&& /*& r0存放bBootFrmNORFlash函数返回值,若返回0表示NAND Flash启动,否则表示在NOR Flash启动& */
&&&&&& beq nand_boot&&&&&&&& /*& 跳转到NAND Flash启动代码& */
/*& NOR Flash启动的代码& */
&&&&&& b&&&& stack_setup&&&&&&&& /* 跳过NAND Flash启动的代码 */
nand_boot:
/*& NAND Flash启动的代码& */
stack_setup:&&&&&&&
&&&&&& /* 其他代码 */
&&&&&& 其中bBootFrmNORFlash函数作用是判断U-Boot是在NAND Flash启动还是NOR Flash启动,若在NOR Flash启动则返回1,否则返回0。根据ATPCS规则,函数返回值会被存放在r0寄存器中,因此调用bBootFrmNORFlash函数后根据r0 的值就可以判断U-Boot在NAND Flash启动还是NOR Flash启动。bBootFrmNORFlash函数在board/samsung/mini2440/nand_read.c中定义如下:
int bBootFrmNORFlash(void)
&&& volatile unsigned int *pdw = (volatile unsigned int *)0;
&&& unsigned int dwV
&&& dwVal = *&&&&&& &&/* 先记录下原来的数据 */
&&& *pdw = 0x;
&&& if (*pdw != 0x)&&&&&& /* 写入失败,说明是在NOR Flash启动 */
&&&&&&& return 1;&&&&&
&&& else&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&& /* 写入成功,说明是在NAND Flash启动 */
&&&&&&& *pdw = dwV&&&&&&& /* 恢复原来的数据 */
&&&&&&& return 0;
&&&& 无论是从NOR Flash还是从NAND Flash启动,地址0处为U-Boot的第一条指令“ b&&& start_code”。
&&&&&& 对于从NAND Flash启动的情况,其开始4KB的代码会被自动复制到CPU内部4K内存中,因此可以通过直接赋值的方法来修改。
&&&&&& 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0,必须通过一定的命令序列才能向NOR Flash中写数据,所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:向地址0写入一个数据,然后读出来,如果发现写入失败的就是NOR Flash,否则就是NAND Flash。
&&&&&& 下面来分析NOR Flash启动部分代码:
208& &&& adr& r0, _start&&&&&&&&&&&&& /* r0 &- current position of code&& */
209& &&& ldr&& r1, _TEXT_BASE&&&&&&&&&&& /* test if we run from flash or RAM */
/* 判断U-Boot是否是下载到RAM中运行,若是,则不用&再复制到RAM中了,这种情况通常在调试U-Boot时才发生 */
210& &&& cmp &&&& r0, r1&&&&& /*_start等于_TEXT_BASE说明是下载到RAM中运行 */
211& &&& beq stack_setup
212& /* 以下直到nand_boot标号前都是NOR Flash启动的代码 */
213& &&& ldr&& r2, _armboot_start&& /*flash中armboot_start的起始地址*/
214& &&& ldr&& r3, _bss_start&&&&&&&& /*uboot_bss的起始地址*/
215& &&& sub& r2, r3, r2&&&&&&&&&&&&& /* r2 &- size of armboot&&uboot实际程序代码的大小&& */
216& &&& add r2, r0, r2&&&&&&&&&&&&& /* r2 &- source end address&&&&&&&& */
217& /*&搬运U-Boot自身到RAM中*/
218& copy_loop:
219& &&& ldmia&&&& r0!, {r3-r10} /* 从地址为[r0]的NOR Flash中读入8个字的数据 */
220& &&& stmia&&&&& r1!, {r3-r10} /* 将r3至r10寄存器的数据复制给地址为[r1]的内存 */
221& &&& cmp&&&&&& r0, r2&&&&&&&&&&&&&&&&&&& /* until source end addreee [r2]&&& */
222& &&& ble& copy_loop
223& &&& b&&&& stack_setup&&&&&&&& /* 跳过NAND Flash启动的代码 */
&&&&&& 下面再来分析NAND Flash启动部分代码:
nand_boot:
&&& mov r1, #NAND_CTL_BASE&
&&& ldr r2, =( (7&&12)|(7&&8)|(7&&4)|(0&&0) )
&&& str r2, [r1, #oNFCONF]&& /* 设置NFCONF寄存器 */
&&&&&& /* 设置NFCONT,初始化ECC编/解码器,禁止NAND Flash片选 */
&&& ldr r2, =( (1&&4)|(0&&1)|(1&&0) )
&&& str r2, [r1, #oNFCONT]&
&&& ldr r2, =(0x6)&&&&&&&&&& /* 设置NFSTAT */
str r2, [r1, #oNFSTAT]
&&&&&& /* 复位命令,第一次使用NAND Flash前复位 */
&&& mov r2, #0xff&&&&&&&&&&&
&&& strb r2, [r1, #oNFCMD]
&&& mov r3, #0&&&&&&&&&&&&&&
&&& /* 为调用C函数nand_read_ll准备堆栈 */
&&& ldr sp, DW_STACK_START&&
&&& mov fp, #0&&&&&&&&&&&&&&
&&& /* 下面先设置r0至r2,然后调用nand_read_ll函数将U-Boot读入RAM */
&&& ldr r0, =TEXT_BASE&&&&& /* 目的地址:U-Boot在RAM的开始地址 */
&&& mov r1, #0x0&&&& &&&&&&&&& /* 源地址:U-Boot在NAND Flash中的开始地址 */
&&& mov r2, #0x30000& &&&&&&& /* 复制的大小,必须比u-boot.bin文件大,并且必须是NAND Flash块大小的整数倍,这里设置为0x3KB) */
&&& bl& nand_read_ll&& &&&&&&&&&&&&& /* 跳转到nand_read_ll函数,开始复制U-Boot到RAM */
tst& r0, #0x0&&&&&&&&&&&&&&&&&&&& /* 检查返回值是否正确 */
beq stack_setup
bad_nand_read:
loop2: b loop2&&& //infinite loop
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
&&&&&& 其中NAND_CTL_BASE,oNFCONF等在include/configs/mini2440.h中定义如下:
#define NAND_CTL_BASE& 0x4E000000& // NAND Flash控制寄存器基址
#define STACK_BASE& 0x33F00000&&&& //base address of stack
#define STACK_SIZE& 0x8000&&&&&&&& //size of stack
#define oNFCONF& 0x00&&&&& /* NFCONF相对于NAND_CTL_BASE偏移地址 */
#define oNFCONT& 0x04&&&&& /* NFCONT相对于NAND_CTL_BASE偏移地址*/
#define oNFADDR& 0x0c&&&& /* NFADDR相对于NAND_CTL_BASE偏移地址*/
#define oNFDATA& 0x10&&&&& /* NFDATA相对于NAND_CTL_BASE偏移地址*/
#define oNFCMD&& 0x08&&&& /* NFCMD相对于NAND_CTL_BASE偏移地址*/
#define oNFSTAT& 0x20&&&&&&& /* NFSTAT相对于NAND_CTL_BASE偏移地址*/
#define oNFECC&& 0x2c&&&&&&&&&&&&& /* NFECC相对于NAND_CTL_BASE偏移地址*/
&&&&&& NAND Flash各个控制寄存器的设置在S3C2440的数据手册有详细说明,这里就不介绍了。
&&&&&& 代码中nand_read_ll函数的作用是在NAND Flash中搬运U-Boot到RAM,该函数在board/samsung/mini2440/nand_read.c中定义。
&&&&&& NAND Flash根据page大小可分为2种: 512B/page和2048B/page的。这两种NAND Flash的读操作是不同的。因此就需要U-Boot识别到NAND Flash的类型,然后采用相应的读操作,也就是说nand_read_ll函数要能自动适应两种NAND Flash。
&&&&&& 参考S3C2440的数据手册可以知道:根据NFCONF寄存器的Bit3(AdvFlash (Read only))和Bit2 (PageSize (Read only))可以判断NAND Flash的类型。Bit2、Bit3与NAND Flash的block类型的关系如下表所示:
表 2.4 NFCONF的Bit3、Bit2与NAND Flash的关系
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Bit2&&& Bit3&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&& & &&&&&& 0&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&& &&& &&& 1&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&& &&&&&&
256 B/page
512 B/page
1024 B/page
2048 B/page
&&&&&& 由于的NAND Flash只有512B/page和2048 B/page这两种,因此根据NFCONF寄存器的Bit3即可区分这两种NAND Flash了。
&&&&&& 完整代码见board/samsung/mini2440/nand_read.c中的nand_read_ll函数,这里给出伪代码:
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
//根据NFCONF寄存器的Bit3来区分2种NAND Flash
&&&&&& if( NFCONF & 0x8 ) &&&&&& /* Bit是1,表示是2KB/page的NAND Flash */
&&&&&&&&&&&&& ////////////////////////////////////
&&&&&&&&&&&&& 读取2K block 的NAND Flash
&&&&&&&&&&&&& ////////////////////////////////////
&&&&&& else&&&&&&&&&&&&&&&&&&&&& /* Bit是0,表示是512B/page的NAND Flash */
&&&&&&&&&&&&& /////////////////////////////////////
&&&&&&&&&&&&& 读取512B block 的NAND Flash
&&&&&&&&&&&&& /////////////////////////////////////
&&& return 0;
(10)设置堆栈
&&&&&& /* &设置堆栈 */
stack_setup:
&&&&&& ldr&& r0, _TEXT_BASE&&&&&&&&&&& /* upper 128 KiB: relocated uboot&& */
&&&&&& sub& r0, r0, #CONFIG_SYS_MALLOC_LEN&& /* malloc area&&&&&&&&&&&&& */
&&&&&& sub& r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /*& 跳过全局数据区&&&&&&&&&&&&&& */
#ifdef CONFIG_USE_IRQ
&&&&&& sub& r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
&&&&&& sub& sp, r0, #12&&&&&&&&&& /* leave 3 words for abort-stack&&& */
&&&&&& 只要将sp指针指向一段没有被使用的内存就完成栈的设置了。根据上面的代码可以知道U-Boot内存使用情况了,如下图所示:
图2.2 U-Boot内存使用情况
(11)清除BSS段
clear_bss:
&&&&&& ldr&& r0, _bss_start&&&&&&&&&&&&& /* BSS段开始地址,在u-boot.lds中指定*/
&&&&&& ldr&& r1, _bss_end&&&&&&&&&&&&&& /* BSS段结束地址,在u-boot.lds中指定*/
&&&&&& mov&&&&&& r2, #0x
clbss_l:str&&&& r2, [r0]&&&&&&&&& /* 将bss段清零*/
&&&&&& add r0, r0, #4
&&&&&& cmp &&&& r0, r1
& ble& clbss_l
&&&&&& 初始值为0,无初始值的全局变量,静态变量将自动被放在BSS段。应该将这些变量的初始值赋为0,否则这些变量的初始值将是一个随机的值,若有些程序直接使用这些没有初始化的变量将引起未知的后果。
154& SMRDATA:&&&&&&&&&&& /*& 下面是13个寄存器的值& */
155& .word& … …
156 & .word& … …
&&&&&& lowlevel_init初始化了13个寄存器来实现RAM时钟的初始化。lowlevel_init函数对于U-Boot从NAND Flash或NOR Flash启动的情况都是有效的。
&&&&&& U-Boot.lds链接脚本有如下代码:
&&&&&& .text :
&&&&&&&&&&&&&&&&&&&& cpu/arm920t/start.o&&& (.text)
&&&& &&&&&&&&&& board/samsung/mini2440/lowlevel_init.o (.text)
&&& &&&&&&&&&&&& board/samsung/mini2440/nand_read.o (.text)
&&&&&&&&&&&&& … …
&&&&&& board/samsung/mini2440/lowlevel_init.o将被链接到cpu/arm920t/start.o后面,因此board /samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代码中。
&&&&&& U-Boot在NAND Flash启动时,lowlevel_init.o将自动被读取到CPU内部4KB的内部RAM中。因此第137~146行的代码将从CPU内部RAM中复制寄存器的值到相应的寄存器中。
&&&&&& 对于U-Boot在NOR Flash启动的情况,由于U-Boot连接时确定的地址是U-Boot在内存中的地址,而此时U-Boot还在NOR Flash中,因此还需要在NOR Flash中读取数据到RAM中。
&&&&&& 由于NOR Flash的开始地址是0,而U-Boot的加载到内存的起始地址是TEXT_BASE,SMRDATA标号在Flash的地址就是SMRDATA-TEXT_BASE。
&&&&&& 综上所述,lowlevel_init的作用就是将SMRDATA开始的13个值复制给开始地址[BWSCON]的13个寄存器,从而完成了存储控制器的设置。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
&问题一:如果换一块开发板有可能改哪些东西?
&&&&&&&&&&&&&&&&& &首先,cpu的运行模式,如果需要对cpu进行设置那就设置,管看门狗,关中断不用改,时钟有可能要改,如果能正常使用则不用改,关闭catch和 MMU不用改,设置bank有可能要改。最后一步拷贝时看地址会不会变,如果变化也要改,执行内存中代码,地址有可能要改。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
问题二:Nor Flash和Nand Flash本质区别:
&&&&&&&&&&&&&&&&& 就在于是否进行代码拷贝,也就是下面代码所表述:无论是Nor Flash还是Nand Flash,核心思想就是将uboot代码搬运到内存中去运行,但是没有拷贝bss后面这段代码,只拷贝bss前面的代码,bss代码是放置全局变量的。 Bss段代码是为了清零,拷贝过去再清零重复操作
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(9)复制U-Boot第二阶段代码到RAM
&&&&&& cpu/arm920t/start.S原来的代码是只支持从NOR Flash启动的,经过修改现在U-Boot在NOR Flash和NAND Flash上都能启动了,实现的思路是这样的:
&&&&&& bl&&& bBootFrmNORFlash /*& 判断U-Boot是在NAND Flash还是NOR Flash启动& */
&&&&&& cmp&&&&&& r0, #0&&&&&&&&& /*& r0存放bBootFrmNORFlash函数返回值,若返回0表示NAND Flash启动,否则表示在NOR Flash启动& */
&&&&&& beq nand_boot&&&&&&&& /*& 跳转到NAND Flash启动代码& */
/*& NOR Flash启动的代码& */
&&&&&& b&&&& stack_setup&&&&&&&& /* 跳过NAND Flash启动的代码 */
nand_boot:
/*& NAND Flash启动的代码& */
stack_setup:&&&&&&&
&&&&&& /* 其他代码 */
&&&&&& 其中bBootFrmNORFlash函数作用是判断U-Boot是在NAND Flash启动还是NOR Flash启动,若在NOR Flash启动则返回1,否则返回0。根据ATPCS规则,函数返回值会被存放在r0寄存器中,因此调用bBootFrmNORFlash函数后根据r0 的值就可以判断U-Boot在NAND Flash启动还是NOR Flash启动。bBootFrmNORFlash函数在board/samsung/mini2440/nand_read.c中定义如下:
int bBootFrmNORFlash(void)
&&& volatile unsigned int *pdw = (volatile unsigned int *)0;
&&& unsigned int dwV
&&& dwVal = *&&&&&& &&/* 先记录下原来的数据 */
&&& *pdw = 0x;
&&& if (*pdw != 0x)&&&&&& /* 写入失败,说明是在NOR Flash启动 */
&&&&&&& return 1;&&&&&
&&& else&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&& /* 写入成功,说明是在NAND Flash启动 */
&&&&&&& *pdw = dwV&&&&&&& /* 恢复原来的数据 */
&&&&&&& return 0;
&&&& 无论是从NOR Flash还是从NAND Flash启动,地址0处为U-Boot的第一条指令“ b&&& start_code”。
&&&&&& 对于从NAND Flash启动的情况,其开始4KB的代码会被自动复制到CPU内部4K内存中,因此可以通过直接赋值的方法来修改。
&&&&&& 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0,必须通过一定的命令序列才能向NOR Flash中写数据,所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:向地址0写入一个数据,然后读出来,如果发现写入失败的就是NOR Flash,否则就是NAND Flash。
&&&&&& 下面来分析NOR Flash启动部分代码:
208& &&& adr& r0, _start&&&&&&&&&&&&& /* r0 &- current position of code&& */
209& &&& ldr&& r1, _TEXT_BASE&&&&&&&&&&& /* test if we run from flash or RAM */
/* 判断U-Boot是否是下载到RAM中运行,若是,则不用&再复制到RAM中了,这种情况通常在调试U-Boot时才发生 */
210& &&& cmp &&&& r0, r1&&&&& /*_start等于_TEXT_BASE说明是下载到RAM中运行 */
211& &&& beq stack_setup
212& /* 以下直到nand_boot标号前都是NOR Flash启动的代码 */
213& &&& ldr&& r2, _armboot_start&& /*flash中armboot_start的起始地址*/
214& &&& ldr&& r3, _bss_start&&&&&&&& /*uboot_bss的起始地址*/
215& &&& sub& r2, r3, r2&&&&&&&&&&&&& /* r2 &- size of armboot&&uboot实际程序代码的大小&& */
216& &&& add r2, r0, r2&&&&&&&&&&&&& /* r2 &- source end address&&&&&&&& */
217& /*&搬运U-Boot自身到RAM中*/
218& copy_loop:
219& &&& ldmia&&&& r0!, {r3-r10} /* 从地址为[r0]的NOR Flash中读入8个字的数据 */
220& &&& stmia&&&&& r1!, {r3-r10} /* 将r3至r10寄存器的数据复制给地址为[r1]的内存 */
221& &&& cmp&&&&&& r0, r2&&&&&&&&&&&&&&&&&&& /* until source end addreee [r2]&&& */
222& &&& ble& copy_loop
223& &&& b&&&& stack_setup&&&&&&&& /* 跳过NAND Flash启动的代码 */
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:14693次
排名:千里之外
转载:12篇
(3)(1)(8)(2)(1)(5)

我要回帖

更多关于 uboot内存分布 的文章

 

随机推荐