1. 前言
本来友善支臂有提供tiny6410的移植好uboot源代码,但是本着学习的态度,总是希望能够自己开始从零开始,一步步自己实现uboot的移植,这样更能了解uboot的编程思想,也能更能了解硬件启动的原理。今后换了硬件平台,脱离了友善支臂,也能够自己独当一面,而不是单纯的复制粘贴。
本文将以最小化修改来达到移植目的。本次移植工程已托管到github,可通过查看提交记录,了解每个步骤的目的与修改了哪些文件
2. 移植的uboot目标版本选择
首次移植应降低难度,应选择与tiny6410最为接近的开发板进行移植,官方uboot中已经包含了诸多三星公司芯片的开发板,其中与tiny6410最为接近的为smdk6400。
下载uboot源代码,可直接克隆u-boot的代码仓库,或者直接在官网直接下载u-boot-v2013.01版本的压缩包,该版本我通过版本库查看,这是支持smdk6400开发板的最后一个版本,后续版本smdk6400就被删了。选择最后一个版本也是因为更加的稳定。
# 克隆uboot仓库
git clone git://www.denx.de/git/u-boot.git
# 检出需要的目标版本
git checkout v2013.01
3. 编译器的选择
我的编译器版本是:gcc-arm-linux-gnueabi version 4.3.2 (Sourcery G++ Lite 2008q3-72)
本来我的电脑已经安装了v6.3.0版本,但是编译时,会产生错误,缺少文件,如下
/home/eric/linux-develop/u-boot/include/linux/compiler-gcc.h:87:30: fatal error: linux/compiler-gcc6.h: 没有那个文件或目录
#include gcc_header(__GNUC__)
这里我先不管编译器的问题,在网上下载了个v4版本的,编译就可以了。后续再试试高版本的能不能行,以防止后续由于编译器的问题出现更多的坑。
环境变量设置:将环境变量设置添加的bashrc文件中,避免每次使用都要设置
vim ~/.bashrc
# 尾部增加如下语句
# 将编译器安装路径添加到PATH
PATH=$PATH:/usr/local/arm/4.3.2/bin/
# 设置交叉编译命令前缀
CROSS_COMPILE=arm-linux-
# 导出环境变量
export CROSS_COMPILE PATH
4. sdmk6400工程编译问题
这里先确保sdmk6400工程能够编译通过,再进行后续的移植工作。
步骤如下:
cd u-boot/
# 清理工程目录
make distclean
# 开发板配置
make smdk6400_config
# 配置日志
eric@eric-PC:~/linux-develop/u-boot$ make smdk6400_config
warning: Please migrate to boards.cfg. Failure to do so will
mean removal of your board in the next release.
Configuring for smdk6400 board...
# 编译
make
4.1 u-boot.lds文件修改
首次编译工程,会报告如下错误
# 官方工程,此时编译会报告错误,日志如下:
arm-linux-ld:u-boot.lds:19: syntax error
Makefile:568: recipe for target 'u-boot' failed
make: *** [u-boot] Error 1
日志报告,u-boot.lds中存在语法错误,这是一个问题,打开u-boot.lds第19行,发现链接文件中存在几处对齐小写的align,而大写的ALIGN才是合法的,因此需要将链接文件的中align修改为全大写格式。
. = ALIGN(4);
.got : { *(.got) }
. = align(4);
但并不是修改工程根目录下的u-boot.lds,此处的u-boot.lds也是从别处拷贝过来的,拷贝的源头可查看Makefile中以下语句
# If there is no specified link script, we look in a number of places for it
ifndef LDSCRIPT
ifeq ($(CONFIG_NAND_U_BOOT),y)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
endif
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot.lds
# We don't expect a Makefile here
LDSCRIPT_MAKEFILE_DIR =
endif
ifeq ($(wildcard $(LDSCRIPT)),)
$(error could not find linker script)
endif
endif
本项目中应该修改board/samsung/smdk6400/u-boot-nand.lds文件,将align更改为ALIGN.
4.2 nand_spl文件夹下Makefile文件修改
链接脚本修改完成后,再次make进行编译,会出现如下错误,提示"_main"未定义
start.o: In function `cpu_init_crit':
/home/eric/linux-develop/u-boot/nand_spl/board/samsung/smdk6400/start.S:227: undefined reference to `_main'
Makefile:59: recipe for target '/home/eric/linux-develop/u-boot/nand_spl/u-boot-spl' failed
make[1]: *** [/home/eric/linux-develop/u-boot/nand_spl/u-boot-spl] Error 1
make[1]: Leaving directory '/home/eric/linux-develop/u-boot/nand_spl/board/samsung/smdk6400'
Makefile:602: recipe for target 'nand_spl' failed
make: *** [nand_spl] Error 2
这时,需要修改nand_spl/board/samsung/smdk6400/Makefile文件
修改前
SOBJS = start.o cpu_init.o lowlevel_init.o
修改后,添加crt0.o
SOBJS = start.o cpu_init.o lowlevel_init.o crt0.o
在start.S目标底下添加crt0.S目标
# from cpu directory
$(obj)start.S:
@rm -f $@
@ln -s $(TOPDIR)/arch/arm/cpu/arm1176/start.S $@
# 以下为新增规则,链接crt0.S
$(obj)crt0.S:
@rm -f $@
@ln -s $(TOPDIR)/arch/arm/lib/crt0.S $@
4.2 crt0.S文件修改
Makefile文件修改后,再次编译,会出现如下错误
crt0.o: In function `clbss_l':
/home/eric/linux-develop/u-boot/nand_spl/board/samsung/smdk6400/crt0.S:153: undefined reference to `coloured_LED_init'
/home/eric/linux-develop/u-boot/nand_spl/board/samsung/smdk6400/crt0.S:154: undefined reference to `red_led_on'
Makefile:59: recipe for target '/home/eric/linux-develop/u-boot/nand_spl/u-boot-spl' failed
make[1]: *** [/home/eric/linux-develop/u-boot/nand_spl/u-boot-spl] Error 1
make[1]: Leaving directory '/home/eric/linux-develop/u-boot/nand_spl/board/samsung/smdk6400'
Makefile:602: recipe for target 'nand_spl' failed
make: *** [nand_spl] Error 2
修改arch/arm/lib/crt0.S
添加如下宏定义,可以解决该问题
#ifndef CONFIG_NAND_SPL
bl coloured_LED_init bl coloured_LED_init
bl red_led_on bl red_led_on
#endif
至此,smdk6400工程编译就不会出现报错现象。可进入下一步tiny6410移植。
5. tiny6410开发板相关目录文件建立
先清理工程目录,执行make distclean
5.1 项目根目录Makefile文件修改
参考以下内容,建立tiny6410配置目标规则
smdk6400_noUSB_config \
smdk6400_config : unconfig
@mkdir -p $(obj)include $(obj)board/samsung/smdk6400
@mkdir -p $(obj)nand_spl/board/samsung/smdk6400
@echo "#define CONFIG_NAND_U_BOOT" > $(obj)include/config.h
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
@if [ -z "$(findstring smdk6400_noUSB_config,$@)" ]; then \
echo "RAM_TEXT = 0x57e00000" >> $(obj)board/samsung/smdk6400/config.tmp;\
else \
echo "RAM_TEXT = 0xc7e00000" >> $(obj)board/samsung/smdk6400/config.tmp;\
fi
@$(MKCONFIG) smdk6400 arm arm1176 smdk6400 samsung s3c64xx
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
添加以下内容
tiny6410_noUSB_config \
tiny6410_config : unconfig
@mkdir -p $(obj)include $(obj)board/samsung/tiny6410
@mkdir -p $(obj)nand_spl/board/samsung/tiny6410
@echo "#define CONFIG_NAND_U_BOOT" > $(obj)include/config.h
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
@if [ -z "$(findstring tiny6410_noUSB_config,$@)" ]; then \
echo "RAM_TEXT = 0x57e00000" >> $(obj)board/samsung/tiny6410/config.tmp;\
else \
echo "RAM_TEXT = 0xc7e00000" >> $(obj)board/samsung/tiny6410/config.tmp;\
fi
@$(MKCONFIG) tiny6410 arm arm1176 tiny6410 samsung s3c64xx
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
5.2 include/configs/tiny6410.h开发板配置文件建立
cd include/configs/
cp smdk6400.h tiny6410.h
5.3 board目录下文件建立
cd board/samsung/
# 拷贝文件夹
cp -r smdk6400/ tiny6410/
# 文件更名
mv smdk6400.c tiny6410.c
mv smdk6400_nand_spl.c tiny6410_nand_spl.c
# 修改Makefile文件,将smdk6400.o替换为tiny6410.o
vim Makefile
5.4 nand_spl目录下文件建立
cd nand_spl/board/samsung/
# 拷贝文件夹
cp -r smdk6400/ tiny6410/
# 修改Makefile文件,将smdk6400全部替换为tiny6410
vim Makefile
# 可使用以下命令进行替换
:1,$s/smdk6400/tiny6410/g
至此,文件建立完成,可以试下编译是否成功,编译不成功,一般为文件名与Makefile中的依赖不对应
# 配置
make tiny6410_config
# 编译
make
6. uboot工程代码修改
6.1 第一步,将UBOOT跑起来
6.1.1 board/samsung/tiny6410/lowlevel_init.S
文件修改
该文件需要修改LED点亮代码,与时钟系统配置代码
<1> LED灯显示,方便初期调试,4个LED,这里设置为间隔点亮
lowlevel_init:
mov r12, lr
/* LED */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x11110000
str r1, [r0, #GPKCON0_OFFSET]
ldr r1, =0x5555aa55
str r1, [r0, #GPKPUD_OFFSET]
ldr r1, =0x000000a0
str r1, [r0, #GPKDAT_OFFSET]
<2> 时钟系统配置修改
以下代码在lowlevel_init.S
文件185~200行之间,该部分代码本可以修改配置文件tiny6410.h中的CONFIG_S3C6400宏定义,但是该宏多处地方使用,为减少目前的移植工作,可直接将以下代码中的预编译命令删除。
#ifndef CONFIG_S3C6400 /* 此行删除 */
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0xC0
orr r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET]
wait_for_async:
ldr r1, [r0, #OTHERS_OFFSET]
and r1, r1, #0xf00
cmp r1, #0x0
bne wait_for_async
#endif /* 此行删除 */
6.1.2 arch/arm/include/asm/arch-s3c64xx/s3c6400.h
文件修改
修改APLL, MPLL分频系数
// 775~779行
#elif defined(CONFIG_CLK_533_133_66)
#define STARTUP_AMDIV 266
#define STARTUP_MDIV 266
#define STARTUP_PDIV 3
#define STARTUP_SDIV 1
6.1.3 drivers/serial/s3c64xx.c 文件修改
这部分为串口功能的修改,这部分原理上是不需要修改的,但是由于我这边目前是使用友善之臂提供的uboot将本次移植的uboot下载到DRAM中直接运行,但由于友善之臂uboot串口所使用的使用,与本uboot时钟不同,且根据s3c6410芯片手册上描述了,时钟切换的特殊要求,因此需要做出如下修改:
static int s3c64xx_serial_init(void)
{
s3c64xx_uart *const uart = s3c64xx_get_base_uart(UART_NR);
/* reset and enable FIFOs, set triggers to the maximum */
uart->UFCON = 0xff;
uart->UMCON = 0;
/* 8N1 */
uart->ULCON = 3;
/* No interrupts, no DMA, pure polling */
if (uart->UCON & 0xC00 == 0xC00)
uart->UCON = (2<<10) | 0x5;
else
uart->UCON = 5;
serial_setbrg();
return 0;
}
在S3C6410数据手册中,关于串口波特率时钟描述有如下一段,
- 当串口波特率时钟从EXT_UCLK0切换到PCLK时,时钟设置需设置为2‘b00
- 当串口波特率时钟从EXT_UCLK1切换到PCLK时,时钟设置需设置为2‘b10
When you want to change EXT_UCLK0 to PCLK for UART baudrate , clock selection field must be set to 2’b00. But, when you want to change EXT_UCLK1 to PCLK for UART baudrate , clock selection field must be set to 2’b10.
因为之前的uboot所使用的时钟为EXT_UCLK1,因此在本uboot中对UCON寄存器的设置,时钟选择需设置为2’b10
6.1.4 uboot地址修改
将uboot链接地址由0x57e00000修改到0x50000000,这样方便下载到DRAM后,可以直接运行uboot,方便前期调试
<1> uboot根目录Makefile修改链接地址
tiny6410_config : unconfig
@mkdir -p $(obj)include $(obj)board/samsung/tiny6410
@mkdir -p $(obj)nand_spl/board/samsung/tiny6410
@echo "#define CONFIG_NAND_U_BOOT" > $(obj)include/config.h
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
@if [ -z "$(findstring tiny6410_noUSB_config,$@)" ]; then \
echo "RAM_TEXT = 0x50000000" >> $(obj)board/samsung/tiny6410/config.tmp;\
else \
echo "RAM_TEXT = 0xc0000000" >> $(obj)board/samsung/tiny6410/config.tmp;\
fi
@$(MKCONFIG) tiny6410 arm arm1176 tiny6410 samsung s3c64xx
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
<2> 配置文件tiny6410.h修改宏定义
#define CONFIG_SYS_PHY_UBOOT_BASE (CONFIG_SYS_SDRAM_BASE + 0)
#define CONFIG_SYS_UBOOT_BASE (CONFIG_SYS_MAPPED_RAM_BASE + 0)
至此,将uboot工程编译,并下载DRAM,并go 50000000
;然后uboot串口就会输出一些调试信息,说明uboot已经开始运行起来了。虽然还有一些问题,但后续会一步一步解决。
uboot启动日志
## Starting application at 0x50000000 ...
U-Boot 2013.01-00008-ga1e7b04d3d (Mar 18 2020 - 19:54:29) for TINY6410
CPU: S3C6400@532MHz
Fclk = 532MHz, Hclk = 133MHz, Pclk = 66MHz (ASYNC Mode)
Board: TINY6400
DRAM: 128 MiB
WARNING: Caches not enabled
Flash: *** failed ***
### ERROR ### Please RESET the board ###
6.2 第二步,将UBOOT正常运行
6.2.1 Flash驱动代码关闭
在上一节中,uboot启动日志中报告FLASH错误。查看代码,错误原因是在flash_init()调用中返回flash_size=0,导致后续的代码判断进行系统报错与挂起。由于tiny6410中并未用到norflash,因此可以将Flash驱动代码关闭。
<1> tiny6410.h文件中修改
/* 添加以下宏定义,配置系统无NOR FLASH */
#define CONFIG_SYS_NO_FLASH /* 该宏需要定义在 #include <config_cmd_default.h> 这条语句前即可 */
/* 注释或删掉以下定义 */
#define CONFIG_SYS_MAX_FLASH_BANKS 1 /* max number of memory banks */
#define CONFIG_SYS_MAX_FLASH_SECT 40
#define CONFIG_AMD_LV800
#define CONFIG_SYS_FLASH_CFI 1 /* Use CFI parameters (needed?) */
#define CONFIG_FLASH_CFI_DRIVER 1
#define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_16BIT
#define CONFIG_FLASH_CFI_LEGACY
#define CONFIG_SYS_FLASH_LEGACY_512Kx16
<2> tiny6410.c文件中修改
以下代码删除或注释
ulong board_flash_get_legacy (ulong base, int banknum, flash_info_t *info)
{
if (banknum == 0) { /* non-CFI boot flash */
info->portwidth = FLASH_CFI_16BIT;
info->chipwidth = FLASH_CFI_BY16;
info->interface = FLASH_CFI_X16;
return 1;
} else
return 0;
}
编译,下载,运行
## Starting application at 0x50000000 ...
U-Boot 2013.01-00008-ga1e7b04d3d-dirty (Mar 18 2020 - 20:21:41) for TINY6410
CPU: S3C6400@532MHz
Fclk = 532MHz, Hclk = 133MHz, Pclk = 66MHz (ASYNC Mode)
Board: TINY6400
DRAM: 128 MiB
WARNING: Caches not enabled
NAND: raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
256 MiB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: CS8900-0
Hit any key to stop autoboot: 0
6.2.2 修复raise: Signal # 8 caught问题
该问题解决参考了这里,同时对比了本工程文件(arch/arm/cpu/arm920t/s3c24x0/timer.c)
本次主要在arch/arm/cpu/arm176/s3c64xx/timer.c进行了一些变量替换,如下:
- 添加全局数据变量定义DECLARE_GLOBAL_DATA_PTR
- timer_load_val替换为gd-tbu
- lastdec替换为gd->lastinc
- timestamp替换为gd->tbl
至此,uboot运行就不会出现有异常问题了。
6.3 第三步,网络功能移植
工程中目前所使用的网卡是cs8900,而tiny6410中使用的是dm9000网卡。因此后续需要修改一些内容以支持dm9000网卡功能。
<1> tiny6410.h文件修改
#define CONFIG_CS8900 /* we have a CS8900 on-board */
#define CONFIG_CS8900_BASE 0x18800300
#define CONFIG_CS8900_BUS16 /* follow the Linux driver */
/* 将上面的定义替换为下面这些内容 */
#define CONFIG_DM9000
#define CONFIG_DRIVER_DM9000
#define CONFIG_DM9000_BASE 0x18000300
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE+4)
/*
* U-Boot 网络环境变量定义
*/
#define CONFIG_ETHADDR 08:90:90:90:90:90
#define CONFIG_IPADDR 192.168.1.100
#define CONFIG_SERVERIP 192.168.1.10
#define CONFIG_GATEWAYIP 192.168.1.10
#define CONFIG_NETMASK 255.255.255.0
<2> tiny6410.c文件修改
将cs8900初始化代码修改为dm9000的初始化代码
点击查看修改内容
可尝试使用ping、tftp命令来测试网络功能是否正常,注意ip地址设置需在同一网段。
至此,uboot已经可以实现大部分常用的已经实现了。
7. nand_spl代码修改
SPL(Secondary Program Loader)是指开发板启动后的第二阶段阶段运行程序,它会被三星公司固化的启动代码BL0拷贝到stepping stone中运行,它的功能是将主体的uboot拷贝到DRAM中,并跳转到目标地址运行。
<1> arch/arm/lib/crt0.S
文件修改
之前的编译已经编译出了这部分代码,位于nand_spl文件夹下,u-boot-spl.bin
但目前这部分代码无法正常运行,需要进行一些小的修改,打开arch/arm/lib/crt0.S
,进行如下修改
#if defined(CONFIG_NAND_SPL)
/* deprecated, use instead CONFIG_SPL_BUILD */
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
b here /* 添加这行,跳过后面的代码重定向,直接配置c运行环境,然后运行nand_boot */
#elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
-------------
#if defined(CONFIG_NAND_SPL)
/* call _nand_boot() */
b nand_boot /* 使用b命令相对跳转,防止由于链接地址问题导致无法正确跳转 */
#else
<2> tiny6410.h
文件修改uboot镜像大小
并修改tiny6410.h中的以下宏定义,根据u-boot.bin的大小修改,不能小于u-boot.bin的实际大小。我这里设置大小为254k
#define CONFIG_SYS_NAND_U_BOOT_SIZE (254 * 1024) /* Size of RAM U-Boot image */
8. 更新nand flash中的uboot镜像
nand flash中的区域分布如下
u-boot-spl.bin | u-boot.bin | env | kernel | rootfs |
---|---|---|---|---|
固定4k | 254k | 默认16k | - | - |
<1> 下载镜像
注意u-boot.bin的地址偏移为4k
tftp 50000000 u-boot-spl.bin
tftp 50001000 u-boot.bin
<2> 将镜像保存到nand flash
擦除大小需要根据u-boot.bin的大小,我这假设u-boot.bin大小为254k
# nand flash擦除
nand erase 0 40800
# nand flash 写入
nand write 50000000 0 40800
至此,就实现了对友善之臂uboot的替代,重新上电看看是否能够正常运行。