s3c6410_uboot中的代码重定位(nand->sdram)
本文仅探讨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, #
mov fp, # /* no previous frame, so fp=0 */
mov r9, #0x1000
bl copy_uboot_to_ram : tst r0, #0x0
bne copy_failed ldr r0, =0x0c000000
ldr r1, _TEXT_PHY_BASE
: ldr r3, [r0], #
ldr r4, [r1], #
teq r3, r4
bne compare_failed /* not matched */
subs r9, r9, #
bne 1b : 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 = ;
int i;
vu_char id; NAND_ENABLE_CE();
NFCMD_REG = NAND_CMD_READID;
NFADDR_REG = 0x00; /* wait for a while */
for (i=; i<; i++);
id = NFDATA8_REG;
id = NFDATA8_REG; if (id > 0x80)
large_block = ; /* 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 = ; if (large_block)
page_shift = ; /* Read pages */
for (i = ; i < (0x3c000>>page_shift); i++, buf+=(<<page_shift)) {
nandll_read_page(buf, i, large_block);
} return ;
}
首先根据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 = ; if (large_block)
page_size = ; NAND_ENABLE_CE(); NFCMD_REG = NAND_CMD_READ0; /* Write Address */
NFADDR_REG = ; if (large_block)
NFADDR_REG = ; NFADDR_REG = (addr) & 0xff;
NFADDR_REG = (addr >> ) & 0xff;
NFADDR_REG = (addr >> ) & 0xff; if (large_block)
NFCMD_REG = NAND_CMD_READSTART; NF_TRANSRnB(); /* for compatibility(2460). u32 cannot be used. by scsuh */
for(i=; i < page_size; i++) {
*buf++ = NFDATA8_REG;
} NAND_DISABLE_CE();
return ;
}
从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_uboot中的代码重定位(nand->sdram)的更多相关文章
- S3C2440—10.代码重定位
文章目录 一.启动方式 1.1 NAND FLASH 启动 1.2 NOR FLASH 启动 二. 段的概念 2.1 重定位数据段 2.2 加载地址的引出 三.链接脚本 3.1 链接脚本的引入 3.2 ...
- 【图片+代码】:GCC 链接过程中的【重定位】过程分析
作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 关注下方公众号,回复[书籍],获取 Linux.嵌入式领域经典书籍:回复[PDF],获取所有原创文章( PDF 格式). ...
- 代码重定位和位置无关码——运行于nor flash
通过前面的学习,我们知道,把可执行程序从一个位置复制到另一个位置的过程叫做重定位. 现在有两种方式,第一种是只重定位data段到内存(sdram),为什么需要重定位?因为有些flash的写操作,不是简 ...
- s3c2440裸机-代码重定位(1.重定位的引入,为什么要代码重定位)
1.重定位的引入(为什么要代码重定位) 我们知道s3c2440的cpu从0地址开始取指令执行,当从nor启动时,0地址对应nor,nor可以像内存一样读,但不能像内存一样写.我们能够从nor上取指令执 ...
- s3c2440裸机-代码重定位、清bss的改进和位置无关码
1.代码重定位的改进 用ldr.str代替ldrb, strb加快代码重定位的速度. 前面重定位时,我们使用的是ldrb命令从的Nor Flash读取1字节数据,再用strb命令将1字节数据写到SDR ...
- s3c2440裸机-代码重定位(2.编程实现代码重定位)
代码重定位(2.编程实现代码重定位) 1.引入链接脚本 我们上一节讲述了为什么要重定位代码,那么怎么去重定位代码呢? 上一节我们发现"arm-linux-ld -Ttext 0 -Tdata ...
- Linux从头学06:16张结构图,彻底理解【代码重定位】的底层原理
作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...
- U-Boot中关于TEXT_BASE,代码重定位,链接地址相关说明
都知道U-BOOT分为两个阶段,第一阶段是(~/cpu/arm920t/start.S中)在FLASH上运行(一般情况 下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM ...
- uboot 与 代码重定位
ref: https://blog.csdn.net/dhauwd/article/details/78566668 https://blog.csdn.net/yueqian_scut/articl ...
随机推荐
- git同步开发更新至项目目录(转载)
From:http://toroid.org/ams/git-website-howto 本地版本库中存放开发的项目代码.以下内容介绍如何将本地版本库修改通过执行“git push web”命令同步到 ...
- android之Widget01
ExampleAppWidgetProvider.java package com.example.mars_2600_widget01; import android.appwidget.AppWi ...
- 利用js将 json对象在textarea中赋值与展示
明明很简单的东西,可惜网上一大堆废话.在此记录,转需. jsonStr = JSON.stringify(jsondata,); example: <!doctype html> < ...
- python异常类型
python2: BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- Stop ...
- 105、android:windowSoftInputMode属性详解
activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Android1.5后的一个新特性. 这个属性能影响两件事情: [一]当有焦点产生时,软键盘是隐藏还是显示 [二]是否减少活动 ...
- linux内核设计与实现--进程管理
进程就是出于执行期的程序.进程的另一个名字是任务. 执行线程,简称线程(thread),是在进程中活动的对象.每个线程都有一个独立的程序计数器.进程栈和一组进程寄存器.内核调度的对象是线程,而不是进程 ...
- MongoDB增删改查
MongoDB以文档的形式存储数据,文档是类似于JSON键值对结构的BSON格式. 许多有共性的文档就组成一个集合. 集合.文档分别对应关系型数据库的表和行记录. 进入数据库: [mongodb@lo ...
- 启用 CORS 来解决这个问题(ajax跨域请求)
<input type="file" name="btn_Upload" value="上传" id="btn_Upload ...
- bochs安装一系列问题
http://blog.chinaunix.net/uid-23817499-id-3418083.html http://www.mouseos.com/os/tools/bochs.html ...
- jdk线程的简单使用
一.线程的实现方式方式一:继承Thread类一个类只要继承了Thread类,并重写run()方法,则就可以实现多线程的操作. public class ThreadDemo01 { public st ...