我之前毕业设计是在学校实验室里做的嵌入式开发和fpga设计。当时摸索过程中碰到了许多问题,绕了些弯路,现在早已解题,觉得有必要把我所做的内容发出来,抛砖引玉,为后人提供个参考。在此也感谢帮助过我的前人们,及其博文。写这些博文也是为了将互助的精神发扬光大。
本文参考了以下博客:
http://blog.sina.com.cn/s/blog_3e4774e30100hgyd.html
http://www.cublog.cn/u3/104447/showart_2205510.html
先是移植u-boot-2010.09到三星SC32442平台
U-boot 版本 2010.09
1新建2442配置选项并编译:
解压缩到~/development目录下,进入目录:
hu@hu-laptop:~$ cd ~/development/u-boot-2010.09
创建针对2442板子的配置项:
hu@hu-laptop:~/development/u-boot-2010.09$ cd board/samsung
hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ mkdir board2442
hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ cp -rf smdk2410/* board2442
hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ ls
board2442 goni smdk2400 smdk2410 smdk6400 smdkc100
hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ cd board2442
hu@hu-laptop:~/development/u-boot-2010.09/board/samsung/board2442$ mv smdk2410.c board2442.c
hu@hu-laptop:~/development/u-boot-2010.09/board/samsung/board2442$ cd ../../../
hu@hu-laptop:~/development/u-boot-2010.09$ cp include/configs/smdk2410.h include/configs/board2442.h
hu@hu-laptop:~/development/u-boot-2010.09$ gedit board/samsung/board2442/Makefile
修改 COBJS := smdk2410.o flash.o
为 COBJS := board2442.o flash.o
这个版本的u-boot和以前版本不一样了,新建配置选项不需要修改顶层目录下的Makefile,只需修改board.cfg,在smdk2410下面照样子添加
board2442 arm arm920t - samsung s3c24x0
打开顶层Makefile,设置编译工具链。
# set default to nothing for native builds
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
在此之后加上一行:
CROSS_COMPILE := arm-unknown-linux-gnueabi-
之后就可以编译了,命令行进入顶层目录,配置目标板:
make board2442_config
编译:
make all
之后就能在顶层目录下编译出u-boot, u-boot.bin, u-boot.srec, u-boot.map文件。
2.修改时钟,低级初始化等设定,使串口成功输出信息:
修改arch/cpu/arm920t/start.S:
注释掉无关的LED灯初始化语句:
//bl coloured_LED_init
//bl red_LED_on
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
/* turn off the watchdog */
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#else //下面2410和2440的寄存器地址是一致的
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# endif
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
# if defined(CONFIG_S3C2442b)//添加s3c2440的中断禁止部分
ldr r1, =0xfff //由2442手册设置,与2440不一样,2440是0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
# if defined(CONFIG_S3C2442b) //添加2442时钟部分
#define MPLLCON 0x4C000004 //系统主频配置寄存器基地址
#define UPLLCON 0x4C000008 //USB时钟频率配置寄存器基地址
ldr r0, =CLKDIVN //设置分频系数FCLK:HCLK:PCLK = 1:4:8
mov r1, #5
str r1, [r0]
ldr r0, =MPLLCON //设置系统主频为400MHz
ldr r1, =0x3F040 //63 4 0 这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分
str r1, [r0]
ldr r0, =UPLLCON //设置USB时钟频率为47.98MHz
ldr r1, =0x1A041 //26 4 1这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分
str r1, [r0]
# else
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
# endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
修改
修改include/configs/board2442.h头文件:
添加CONFIG_S3C2442b配置宏
#define CONFIG_ARM920T 1 /* This is an ARM920T Core */
#define CONFIG_S3C24X0 1 /* in a SAMSUNG S3C24x0-type SoC */
#define CONFIG_S3C2410 1 /* specifically a SAMSUNG S3C2410 SoC */
#define CONFIG_SMDK2410 1 /* on a SAMSUNG SMDK2410 Board */
#define CONFIG_S3C2442b 1
//#define CONFIG_SYS_CLK_FREQ 12000000/* the SMDK2410 has 12MHz input clock */
#define CONFIG_SYS_CLK_FREQ 16934400 //我们板子的晶振频率是16.934
#define CONFIG_SYS_PROMPT "board2442 # " //修改提示符信息
修改board/samsung/board2442/board2442.c文件:
修改机器码,为以后启动Linux内核做准备
/* arch number of SMDK2410-Board */
// gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
gd->bd->bi_arch_number = MACH_TYPE_SMDK2440;
除此之外:
#define FCLK_SPEED 2 //设置默认等于2,即下面蓝色代码部分有效
#if FCLK_SPEED==0 /* Fout = 203MHz, Fin = 12MHz for Audio */
#define M_MDIV 0xC3
#define M_PDIV 0x4
#define M_SDIV 0x1
#elif FCLK_SPEED==1 /* Fout = 202.8MHz */
#define M_MDIV 0xA1
#define M_PDIV 0x3
#define M_SDIV 0x1
#elif FCLK_SPEED==2 /* Fout = 400MHz */
#define M_MDIV 0x3F //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION TABLE”部分进行设置
#define M_PDIV 0x4
#define M_SDIV 0x0
#endif
#define USB_CLOCK 2 //设置默认等于2,即下面蓝色代码部分有效
#if USB_CLOCK==0
#define U_M_MDIV 0xA1
#define U_M_PDIV 0x3
#define U_M_SDIV 0x1
#elif USB_CLOCK==1
#define U_M_MDIV 0x48
#define U_M_PDIV 0x3
#define U_M_SDIV 0x2
#elif USB_CLOCK==2 /* Fout = 47.98MHz */
#define U_M_MDIV 0x1A //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION TABLE”部分进行设置
#define U_M_PDIV 0x4
#define U_M_SDIV 0x1
#endif
修改cpu/arm920t/s3c24x0/speed.c
static ulong get_PLLCLK(int pllreg){
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
ulong r, m, p, s;
if (pllreg == MPLL)
r = clk_power->MPLLCON;
else if (pllreg == UPLL)
r = clk_power->UPLLCON;
else
hang();
m = ((r & 0xFF000) >> 12) + 8;
p = ((r & 0x003F0) >> 4) + 2;
s = r & 0x3;
#if defined(CONFIG_S3C2442b)
if(pllreg == MPLL)
{ //参考S3C2440芯片手册上的公式:PLL=(2 * m * Fin)/(p * 2s)
return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s));
}
#endif
return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
}
/* return HCLK frequency */
ulong get_HCLK(void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
#if defined(CONFIG_S3C2442b)
return(get_FCLK()/4);
#endif
return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
}
之后可以吧u-boot通过openOCD下载到RAM里运行了,不过若要u-boot在ram中运行的话最好事先在board.h头文件里定义两个宏:
#define CONFIG_SKIP_LOWLEVEL_INIT 1
#define CONFIG_SKIP_RELOCATE_UBOOT 1
这样u-boot就不会重复执行底层初始化的工作,不然容易使CPU产生异常
通过openOCD将u-boot下载到内存:

接着就能在串口看到提示信息了

这次设置时钟和串口花了不少功夫。首先根据了2442手册上推荐的时钟频率列表选择两个合适的MPLL UPLL频率。

实验室板子的晶振频率是16.9344M,所以选择47.98 和 400两个频率。可以看得出来,2442比2440的主频要低一些,记得2440是支持533主频的。之后我把编译好的镜像通过j-tag下载到RAM中运行,发现串口终于有输出了,不过是乱码,肯定还是哪里时钟没设置对。值得一提的是,要让开发板能在ram运行uboot镜像,需要进行底层的初始化,这个过程可以通过把u-boot.bin再烧到nand中重启开发板来实现。由于此时的u-boot是不支持nand的,所以启动后只能初始化下cpu,串口是看不到信息的。
针对串口输出是乱码的问题,我上网查阅了很多文章,大部分遇到同样问题的人都是PLL和ULL没有设置好,或者是speed.c里get_PLLCLK()和get_HCLK()两个函数没有进行修改。不过我反复检查了自己的代码,没有他们所说的问题,一时间u-boot开发进度停止不前。后来很偶然地发现问题出在board.h里CONFIG_SYS_CLK_FREQ.默认的值是12000000,开发板的晶振是16934400,改过来问题就解决了.
串口能输入信息的话后面的移植工作就方便很多了,可以通过串口输出调试信息来进行调试.在此之前对u-boot的调试都是”摸黑”的,只能通过openocd从内存和cpu的寄存器值来分析问题.并且实验室的电路板不像大部分商业开发板上有4个LED,也无法通过”点灯大法”来调试.
3.添加nand启动
在board.h文件中添加有关nand控制器的定义:
(在最后一句#endif /* __CONFIG_H */之前):
/*
* Nand flash register and envionment variables
*/
#define CONFIG_S3C2442b_NAND_BOOT 1
#define NAND_CTL_BASE 0x4E000000 //Nand Flash配置寄存器基地址,查2440手册可得知
#define STACK_BASE 0x33F00000 //定义堆栈的地址
#define STACK_SIZE 0x8000 //堆栈的长度大小
#define oNFCONF 0x00
#define oNFCONT 0x04 //控制寄存器的基地址(0x4E000004)
#define oNFADDR 0x0c //地址寄存器的基地址(0x4E00000c)
#define oNFDATA 0x10 //数据寄存器的基地址(0x4E000010)
#define oNFCMD 0x08 //指令寄存器的基地址(0x4E000008)
#define oNFSTAT 0x20 //状态寄存器的基地址(0x4E000020)
#define oNFECC 0x2c //ECC寄存器的基地址(0x4E00002c)
另外,这时要把忽略低级初始化的两个宏去掉了,这样才能自己启动
//#define CONFIG_SKIP_LOWLEVEL_INIT 1
//#define CONFIG_SKIP_RELOCATE_UBOOT 1
修改arch/cpu/arm920t/start.s文件,添加nand启动的代码
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
#if 0 //屏蔽掉从flash启动部分
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif
#ifdef CONFIG_S3C2442b_NAND_BOOT
mov r1, #NAND_CTL_BASE //以下是nand控制寄存器的一些初始化
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF] //复位nand控制器
ldr r2, [r1, #oNFCONF]
ldr r2, =( (1<<4)|(0<<1)|(1<<0) )
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
ldr r2, =(0x6) //RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
mov r2, #0xff
strb r2, [r1, #oNFCMD]
mov r3, #0
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT]
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2
str r2, [r1, #oNFCONT]
//get read to call C functions (for nand_read())
ldr sp, DW_STACK_START //为C代码准备堆栈,堆栈的定义在下面
mov fp, #0
//copy U-Boot to RAM
ldr r0, =TEXT_BASE //C代码第一个参数,u-boot在ram中地址,也就是0x33f80000
mov r1, #0x0 //u-boot在nand中起始地址
mov r2, #0x30000 //u-boot大小
bl nand_read_ll //跳转到C函数nand_read_ll
tst r0, #0x0
beq ok_nand_read
bad_nand_read:
loop2: b loop2 //infinite loop
ok_nand_read: //读取成功
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 //4 bytes * 1024 = 4K-bytes
go_next: //核对4k启动内存中数据和nand中是不是一样
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3: b loop3 //infinite loop
#endif //CONFIG_S3C2442b_NAND_BOOT
#endif //CONFIG_SKIP_RELOCATE_UBOOT
_start_armboot: .word start_armboot //在这一句的下面加上DW_STACK_START的定义
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
接下来,在board/samsung/gec2440/目录下新建一个nand_read.c文件,在该文件中来实现上面汇编中要调用的nand_read_ll函数,代码如下:
#include <config.h>
#define NF_BASE 0x4E000000
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NFCONF __REGi(NF_BASE + 0x0 )
#define NFCONT __REGi(NF_BASE + 0x4 )
#define NFCMD __REGb(NF_BASE + 0x8 )
#define NFADDR __REGb(NF_BASE + 0xC )
#define NFDATA __REGb(NF_BASE + 0x10)
#define NFSTAT __REGb(NF_BASE + 0x20)
#define NAND_CHIP_ENABLE (NFCONT &= ~(1<<1))
#define NAND_CHIP_DISABLE (NFCONT |= (1<<1))
#define NAND_CLEAR_RB (NFSTAT |= (1<<2))
#define NAND_DETECT_RB { while(! (NFSTAT&(1<<2)) );}
#define NAND_SECTOR_SIZE 0x800 //这块nand页面大小变成2k了
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
/* low level nand read function */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
{
return -1; //地址不对齐,退出
}
NAND_CHIP_ENABLE; //片选
for(i=start_addr; i < (start_addr + size);)
{
NAND_CLEAR_RB;
NFCMD = 0; //read0指令
NFADDR = 0 & 0xFF; //Column (A[7:0]) = 0
NFADDR = 0 & 0xFF; // A[11:8]
NFADDR = (i >> 11) & 0xFF; // A[19:12]
NFADDR = (i >> 19) & 0xFF; // A[27:20]
NFCMD = 0x30; //read0x30指令
NAND_DETECT_RB;
for(j=0; j < NAND_SECTOR_SIZE; j++, i++)
{
*buf = (NFDATA & 0xFF);
buf++;
}
}
NAND_CHIP_DISABLE;
return 0;
}
接下来,在board/samsung/board2442/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,如下:
COBJS := gec2440.o flash.o nand_read.o
还有一个重要的地方要修改,在arch/cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的。如下:
.text :
{
cpu/arm920t/start.o (.text)
board/samsung/board2442/lowlevel_init.o (.text)
board/samsung/board2442/nand_read.o (.text)
*(.text)
}
现在终于能启动了

2442的nand和以前移植的2440不一样。不仅仅是一个片内,一个片外的区别,2442的nand是K9F1g08,一个page 2k字节,一个block 64个page,一共又有1024个block,所以一共有128m的大小。2440的nand是k9S1208,一个page 512字节, 总共64M。除了页面,大小不一样外,2442的nand读数据要多一个时钟周期,不仅需要read0指令,也需要read 0x30 指令。刚开始做nand的时候不知道指令周期的区别,只是简单的运用2440的读nand程序,没有read 0x30指令,数据读不出来,使得nand一直处于等待状态,u-boot停止启动。
我后来意识到有可能是nand芯片不一样,上网搜索了一下,参考了个天津大学的硕士论文,加上了read 0x30指令。nand终于能读了,不过u-boot每次进入start_armboot就会出现数据异常的错误,还是启动不了。通过调试工具的单步运行,检查寄存器的值,我发现内存0x33f80850的值很奇怪,是0x09090909. 打印内存后才发现,只有nand前2k的数据正确搬移了,后面出现了大面积的数据错误。
仔细静下心来分析,发现u-boot只成功搬移了前一个page的数据,后面的数据要不错位了(比如ram中0x800的数据又重复了nand中0x400的),要不就是空的(16个字的0xffffffff连着八个字的0x09090909)。这个问题说明是2442的nand页面配置不同导致了其寻址方式和2440nand也不同了。上网找了半天,找到个2442thumb汇编的bootloader,里面有读nand的c程序。对比了以下,果然nand地址寄存器的赋值方式不一样。对照着改了一下,nand终于正常读数了,u-boot也终于启动了。
本文详细记录了U-Boot在三星SC32442平台的移植过程及NAND闪存启动的实现方法。包括配置项创建、时钟设定、串口调试、NAND控制器初始化和数据读取等关键步骤。
:时钟和nand启动&spm=1001.2101.3001.5002&articleId=6579439&d=1&t=3&u=3d7f4aedf2bb40b7a663a50b0457d7c7)
1630

被折叠的 条评论
为什么被折叠?



