本文仅探讨s3c6410从nand flash启动u-boot时的代码重定位过程
参考:
1)《USER'S MANUAL-S3C6410X》第二章 MEMORY MAP 第八章 NAND FLASH CONTROLLER
2)u-boot源码:
u-boot-x.x.x/board/samsumg/smdk6410/lowlevel_init.S
u-boot-x.x.x/cpu/s3c64xx/start.S
u-boot-x.x.x/cpu/s3c64xx/nand_cp.c
代码重定位过程简述
由于在nand flash中无法运行代码,所以当开发板从nand flash启动时,我们需要将存储在外设nand flash中的u-boot代码搬运到sdram中运行,如何完成这个搬运工作呢?这需要借助一个跳板,即“stepping stone”,它是s3c6410的一块内置sram,开发板上电时,nand flash控制器自动将nand flash的前8K的内容拷贝到sram中并执行,这一小段启动代码除了初始化硬件外,最重要的一个工作就是将nand flash中的所有u-boot代码拷贝(即重定位)到sdram的指定地址上去,然后跳转到sdram中执行。
重定位代码解析:
1)nand接口初始化
u-boot启动时,首先执行相应硬件平台的start.S,start.S中调用lowlevel_init对时钟,uart,nand,mmu等底层硬件作初始化。
start.S:
...
bl lowlevel_init /* go setup pll,mux,memory */
...
lowlevel_init.S:
...
/*
* Nand Interface Init for SMDK6400 */
nand_asm_init:
ldr r0, =ELFIN_NAND_BASE
ldr r1, [r0, #NFCONF_OFFSET]
orr r1, r1, #0x70
orr r1, r1, #0x7700
str r1, [r0, #NFCONF_OFFSET]
ldr r1, [r0, #NFCONT_OFFSET]
orr r1, r1, #0x03
str r1, [r0, #NFCONT_OFFSET]
mov pc, lr
...
2)代码重定位
从nand flash启动时,重定位代码如下:
start.S:
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip flash copy */
#ifdef CONFIG_BOOT_NAND
mov r0, #0x1000
bl copy_from_nand
#endif
r1存放当前代码运行的起始地址,r2存放u-boot即将在sdram中运行的地址,如果两个地址相等,说明此时u-boot已经在sdram中运行了,无需再执行从nand拷贝数据到sdram的动作;否则,此时u-boot还在它的临时住所sram中执行,此地不可久留,需要执行copy_from_nand将u-boot代码完完整整地拷贝到sdram中去,然后跳转到sdram中去执行剩下的代码。
/*
* copy U-Boot to SDRAM and jump to ram (from NAND or OneNAND)
* r0: size to be compared
* Load 1'st 2blocks to RAM because U-boot's size is larger than 1block(128k) size
*/
.globl copy_from_nand
copy_from_nand:
mov r10, lr /* save return address */
mov r9, r0
/* get ready to call C functions */
ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
sub sp, sp, #12
mov fp, #0 /* no previous frame, so fp=0 */
mov r9, #0x1000
bl copy_uboot_to_ram
3: tst r0, #0x0
bne copy_failed
ldr r0, =0x0c000000
ldr r1, _TEXT_PHY_BASE
1: ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne compare_failed /* not matched */
subs r9, r9, #4
bne 1b
4: mov lr, r10 /* all is OK */
mov pc, lr
copy_failed:
nop /* copy from nand failed */
b copy_failed
compare_failed:
nop /* compare failed */
b compare_failed
真正执行拷贝动作的是copy_uboot_to_ram函数,它定义在u-boot-x.x.x/cpu/s3c64xx/nand_cp.c中,
int copy_uboot_to_ram (void)
{
int large_block = 0;
int i;
vu_char id;
NAND_ENABLE_CE();
NFCMD_REG = NAND_CMD_READID;
NFADDR_REG = 0x00;
/* wait for a while */
for (i=0; i<200; i++);
id = NFDATA8_REG;
id = NFDATA8_REG;
if (id > 0x80)
large_block = 1;
/* read NAND Block.
* 128KB ->240KB because of U-Boot size increase. by scsuh
* So, read 0x3c000 bytes not 0x20000(128KB).
*/
return nandll_read_blocks(CFG_PHY_UBOOT_BASE, 0x3c000, large_block);
}
nand flash支持两种页大小,512B和2KB,large_block = 0时,页大小为512字节,large_block = 1时,页大小为2K字节。nandll_read_blocks拷贝nand flash从第0页开始的0x3c00(240K)大小的数据到sdram的CFG_PHY_UBOOT_BASE地址处。
/*
* Read data from NAND.
*/
static int nandll_read_blocks (ulong dst_addr, ulong size, int large_block)
{
uchar *buf = (uchar *)dst_addr;
int i;
uint page_shift = 9;
if (large_block)
page_shift = 11;
/* Read pages */
for (i = 0; i < (0x3c000>>page_shift); i++, buf+=(1< } return 0; } 首先根据large_block判断nand flash一个页的大小,从而计算需要拷贝的页的数量,即需要拷贝(0x3c000>>page_shift)个页,nandll_read_page每次只拷贝一个页的数据。 /* * address format * 17 16 9 8 0 * -------------------------------------------- * | block(12bit) | page(5bit) | offset(9bit) | * -------------------------------------------- */ static int nandll_read_page (uchar *buf, ulong addr, int large_block) { int i; int page_size = 512; if (large_block) page_size = 2048; NAND_ENABLE_CE(); NFCMD_REG = NAND_CMD_READ0; /* Write Address */ NFADDR_REG = 0; if (large_block) NFADDR_REG = 0; NFADDR_REG = (addr) & 0xff; NFADDR_REG = (addr >> 8) & 0xff; NFADDR_REG = (addr >> 16) & 0xff; if (large_block) NFCMD_REG = NAND_CMD_READSTART; NF_TRANSRnB(); /* for compatibility(2460). u32 cannot be used. by scsuh */ for(i=0; i < page_size; i++) { *buf++ = NFDATA8_REG; } NAND_DISABLE_CE(); return 0; } 从nand flash中读取数据的流程为片选(NAND_ENABLE_CE)->发读命令(NFCMD_REG)->发地址(NFADDR_REG)->发读命令(NFCMD_REG)->等待数据可读(NF_TRANSRnB)->读数据(NFDATA8_REG)。由于每次从NFDATA8_REG中只可读取1个字节的数据,所以拷贝一页需要读取512或2048次。 当执行完copy_uboot_to_ram返回到start.S时,nand flash中的代码重定位便完成了,此后程序跳转到sdram中执行,stepping stone的职责就此结束。
上一篇:s3c6410_MMU地址映射过程详述
下一篇:s3c6410_uart初始化及读写
推荐阅读最新更新时间:2024-11-11 15:02
设计资源 培训 开发板 精华推荐
- 具有双输入的 LTC3126IUFD 12V、1MHz 降压转换器的典型应用电路
- LTC6400-8 的典型应用 - 用于 DC-300MHz 的 2.2GHz 低噪声、低失真差分 ADC 驱动器
- LTM8073EY -5Vout 从 3.4 到 55Vin 正负转换器的典型应用电路
- 万森S-60-6开关电源
- 四轴空心杯飞控板
- 60WLEDBULBGEVB:可调光、9.5 W、120 Vac CCR 照明电路评估板
- LTM4630EV 2 相、1.5V、36A 输出 DC/DC 稳压器的典型应用电路
- SC2612E 500kHz 降压型 DC/DC 转换器的典型应用
- 10W、3-LED 通用汽车 LED 驱动器
- ESP32数控电源
- 趣味电子技术史话第二讲:开关电源的早期历史
- 邀您观看 微信直播:户外照明智能互连解决方案 让TE连接光明与智能未来
- MPS 隔离式稳压 DCDC 模块——MIE系列,小且不凡!痛点讨论|你理想中的电源模块是怎样的?
- 泰克测试精英养成计划——答题赢好礼
- 有奖直播|恩智浦LPC553x在双电机控制中的应用
- 推陈出“芯“——TI 带你领略智能手机黑科技在线直播 预报名+看直播 好礼让你嗨翻全场!
- 有奖直播|基于Source-down技术的全新英飞凌MOSFET,有效提升功率密度,肉眼可见
- 100%获奖,EE新年福利!
- 英特尔FPGA可编程加速平台介绍,走近AI、数据中心、基因工程等大咖工程
- “泰”想开车 智能篇:智能网联汽车,实现无忧驾驶