汇总点这
前言 本篇是记录移植u-boot (2020.7)的过程和思路,其实有不少好文章已经在描述移植u-boot到JZ2440了,写的又比我好,比如我收藏参考的文章移植u-boot-2019.10到jz2440。
但是本文想尽可能修改更少的代码,保留更多官方的代码,能用既用,至于像更细节的怎样重定位才能更好地支持JZ2440,那应该会留到另一篇进阶的文章。
u-boot是什么就不废话了,开干吧。
百度u-boot,去官网,点源码,去FTP服务器直接去下载最新的就好。其实移植的时候最好找个相似的单板进行移植,不然得全部重新写。但是下载后才发现,最新2020.7版本的uboot已经不支持相似单板smdk2410了。
愤怒的我把历史所有版本下载下来,发现最后的支持在2016.11版本,那我们就先移植到这个版本,再移植到2020.7。
新u-boot 配新编译工具,其实直接 apt 下载的arm-linux-gnueabi-gcc 就能完美编译 2016.11 u-boot(ubuntu16)。
使用命令 sudo apt-get install gcc-arm-linux-gnueabi
,成功安装后使用arm-linux-gnueabi-gcc -v
显示
gcc version 5.4.0 20160609 (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.9)
就对了,工具刚好也是2016年的,这里也是用这个版本。
使用命令 make smdk2410_config
进行配置,命令成功后会生产 .config 文件,这个文件就是配置文件。
要用自己的交叉编译工具编译,使用命令 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
或者直接去Makefile改也行,这里是使用命令 进行编译,命令不能打错,打错命令会使用默认配置gcc。
连一个警告都没有,完美编译。
看到编译完成,我啪的一下就烧进去了,很快啊!开发板没有任何反应,我大意了,没有改,板子没有正常启动。什么都没改当然不兼容了,这就烧进去了,这好吗?这不好。
接下来需要建立一个source insight工程,方便我们移植。因为u-boot也兼容其他的板子,为了避免杂乱,我们只加入自己的板子的信息。
arm -> cpu ->arm920t -> 该目录下的文件 和 s3c24x0
asm -> 该目录下的文件 和 arch-s3c24x0目录
board -> Samsung -> smdk2410
目录smdk2410.h
文件其他全加
首先系统烧进去没有任何反应,就是板子没有正常启动,正常启动后应该会有串口输出,串口输出的前提是串口初始化了,板子cpu初始化了(包括时钟),保证代码运行了(重定位、内存初始化)。那么最实在的方法就是从头看看有什么问题。
那么分析源码首先得找到程序入口,就需要从链接文件入手。链接文件是编译过程中产生的,编译后根目录下会出现一个u-boot.lds
的文件,这就是链接文件。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.__image_copy_start)
*(.vectors)
arch/arm/cpu/arm920t/start.o (.text*)
*(.text*)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data*)
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
. = ALIGN(4);
...此处省略下文
.
代表此处,可以看到一开始程序就在 0 地址运行,这么说应该就只能nor flash启动了。然后u-boot分为几个段:text
, rodata
, data
, u_boot_list
…
而text
就是程序段,搜索 __image_copy_start
发现,其是这都只是数组,那么真正的代码从 start.S
开始。
这个 start.S
在 ...\arch\arm\cpu\arm920t
目录下,事实上u-boot中有很多start.S,第二步建立工程就是分开这些文件,我们用的是arm920t
专属的start.S。
从reset
开始看
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
第一段是把CPU设置成SVC32模式。
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
/*
* relocate exception table
*/
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
由于这里没有定义 CONFIG_AT91RM9200DK
,CONFIG_AT91RM9200EK
。所以这一段是忽略的
#ifdef CONFIG_S3C24X0
/* turn off the watchdog */
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interrupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#else
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interrupt-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
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C24X0 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
这一段开始是s3c2410的初始化,包括 关看门狗, 中断初始化, 时钟初始化
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
这里说,如果没有定义 CONFIG_SKIP_LOWLEVEL_INIT
,便跳到 cpu_init_crit
这个段,我们当然没有定义。这段位于 start.S
后半部分。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 1 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
bl lowlevel_init
mov lr, ip
#endif
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
如果没有定义 CONFIG_SKIP_LOWLEVEL_INIT
和 CONFIG_SKIP_LOWLEVEL_INIT_ONLY
这两个宏,这段代码全生效,这里其实是对cpu的 底层初始化的相关配置,注意在这段代码最后 bl lowlevel_init
才是底层初始化,这个段位于 board\samsung\smdk2410\lowlevelinit.S
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, =CONFIG_SYS_TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0x32
.word 0x30
.word 0x30
根据注释这里是 memory control configuration,内存的初始化。这里有句注释 everything is fine now ,意味着相关配置已经完毕(并不)。执行完这段代码后,跳回 start.S
, 之后start.S
只剩下一句话 bl _main
,这个段位于 D:\毕业班\u-boot-2016.11_test\arch\arm\lib\crt0.S
,至此,start.S
完毕。
至今为止,代码依次做了
start.S
-> set the cpu to SVC32 mode //设置SVC32 模式
-> turn off the watchdog //关闭看门狗
-> ask all IRQs by setting all bits in the INTMR - default //设置中断
-> default FCLK //设置时钟
-> CONFIG_LOWLEVEL_INIT //配置底层
-> flush v4 I/D caches
-> disable MMU stuff and caches
-> setup RAM timing
跳转-> lowlevel.S //跳到 lowlevel.S 初始化内存
-> LOWLEVEL_INIT
-> memory control configuration
到目前为止,和之前的 jz2440 u-boot 对比发现,
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
...
//源码没有定义CONFIG_SPL_BUILD 和 CONFIG_SPL_STACK
//便追溯CONFIG_SYS_INIT_SP_ADDR
#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - \
GENERATED_GBL_DATA_SIZE)
...
//追溯 PHYS_SDRAM_1
#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
...
//追溯 GENERATED_GBL_DATA_SIZE
DEFINE(GENERATED_GBL_DATA_SIZE,
(sizeof(struct global_data) + 15) & ~15)
文件一开球就设置了栈
mov r0, sp
bl board_init_f_alloc_reserve
mov sp, r0
/* set up gd here, outside any C code */
mov r9, r0
bl board_init_f_init_reserve
设置完栈之后就做了两件事:Allocate reserved space 和 Initialize reserved space。
mov r0, #0
bl board_init_f
跳转到board_init_f
,里面有一句
...
if (initcall_run_list(init_sequence_f))
hang();
...
而这个 init_sequence_f
实际是个初始化函数的函数队列(好像就是个数组),而其中有一个函数board_early_init_f
,是单板早期初始化函数。
...
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
...
跳到board_early_init_f
#define FCLK_SPEED 1
#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 = 400MHz */
#define M_MDIV 0xA1
#define M_PDIV 0x3
#define M_SDIV 0x1
#endif
#define USB_CLOCK 1
#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
#endif
...
int board_early_init_f(void)
{
struct s3c24x0_clock_power * const clk_power =
s3c24x0_get_base_clock_power();
struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
/* to reduce PLL lock time, adjust the LOCKTIME register */
writel(0xFFFFFF, &clk_power->locktime);
/* configure MPLL */
writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
&clk_power->mpllcon);
/* some delay between MPLL and UPLL */
pll_delay(4000);
/* configure UPLL */
writel((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV,
&clk_power->upllcon);
/* some delay between MPLL and UPLL */
pll_delay(8000);
/* set up the I/O ports */
...
...
return 0;
}
搜索s3c24x0_get_base_clock_power()
时发现,居然有s3c2440.h
的头文件,说明还是有对S3C2440的支持的。
至此,和之前对比发现时钟设置确实需要重新设置。
首先是发现了比较重要的,异步模式需要设置和时钟频率要改。这里需要参考S3C2440的芯片手册,下面简称参考手册。
FCLK:HCLK:PCLK = 1:2:4
,起作用的是这段位于start.S
代码,因为在参考手册的特性里介绍了S3C2440的工作频率,Fclk最高400MHz,Hclk最高136MHz,Pclk最高68MHz,而我们又想设置设置Fclk 400MHz,那么最好设置为FCLK:HCLK:PCLK = 1:4:8
,修改代码。:#define CLKDIVN 0x4C000014 /* clock divisor register */
...
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3 //这里需要改成-> mov r1, #5
str r1, [r0]
由于smdk2410和我们的单板工作频率设置也不一样,这里 board\samsung\smdk2410\smdk2410.c
需要也需要修改代码,不详细写啦。
...
#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 = 400MHz */
#define M_MDIV 0xA1 // 需要改成 -> #define M_MDIV 0x5C
#define M_PDIV 0x3 // 需要改成 -> #define M_PDIV 0x1
#define M_SDIV 0x1 // 需要改成 -> #define M_SDIV 0x1
#endif
...
/* configure MPLL */
writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
&clk_power->mpllcon);
...
最后在arch\arm\cpu\arm920t\start.S
加上异步模式设置的代码。
...
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
// -> 加上这一段代码
/* 设置CPU工作于异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
...
重新执行三步,然后烧写。
make distclean
make smdk2410_config
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- all
烧写重启后屏幕出现以下乱码,其实是好事,说明单板开始工作了,只是工作不正常而已~
怘▒怘▒ff▒▒▒▒▒
x▒▒▒▒▒▒▒▒~▒▒xxx▒▒▒▒~
▒▒▒▒▒▒▒▒▒▒▒▒怘▒fff▒▒~▒x
▒▒▒▒x▒▒▒▒▒x▒▒▒f▒▒▒▒▒▒▒▒▒x▒▒▒f▒▒▒f▒▒▒▒▒▒▒▒▒▒f▒▒▒▒▒ff▒▒
▒f▒f▒▒▒f▒▒▒▒▒▒▒▒▒▒f▒▒▒f▒▒▒▒▒`▒▒▒▒▒▒▒fx怘▒x▒▒▒▒▒▒▒f~f▒▒f▒▒▒▒▒▒`▒▒f▒f▒▒▒▒▒▒▒▒▒▒f▒▒▒▒▒▒fx▒▒▒▒▒▒▒▒▒▒f▒▒xf▒xf▒`▒▒▒▒xf▒▒▒▒f▒▒`f▒▒▒怘▒▒▒▒▒▒fxf▒▒怘▒▒▒f▒▒▒fxf▒▒怘▒f▒▒▒▒▒fxf▒▒怘▒▒f▒▒▒▒▒▒▒▒▒▒▒f▒▒▒▒▒~▒▒▒▒▒▒▒▒▒x▒▒fx▒▒▒▒▒
▒▒fx▒▒怘▒怘▒f
▒▒▒`
我的乱码是这样子的,不知道大家的会不会和我一样呢
一般出现乱码,其实都只是波特率不正常而已,而波特率又是根据时钟频率设定的。
之前发现了s3c2440.h
头文件,最好我们就是编译s3c2440
的参数。
但是现在肯定编译的是s3c2410.h
的头文件,问题是怎么改成编译s3c2440.h
头文件呢。
在linux下u-boot根目录执行查找命令
grep s3c2440 * -nR
//喜大普奔,输出很多相关信息,眼利的发现有一句话
...
arch/arm/include/asm/arch/s3c24x0_cpu.h:13: #include <asm/arch/s3c2440.h>
...
打开这个文件:
/*
* (C) Copyright 2009
* Kevin Morfitt, Fearnside Systems Ltd,
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifdef CONFIG_S3C2400
#include
#elif defined CONFIG_S3C2410
#include
#elif defined CONFIG_S3C2440
#include
#else
#error Please define the s3c24x0 cpu type
#endif
找到了!现在源码肯定是定义了CONFIG_S3C2410
,所以我们要继续搜索 CONFIG_S3C2410
grep '#define CONFIG_S3C2410' * -nR
...
输出信息:
include/configs/VCMA9.h:25:#define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */
include/configs/smdk2410.h:21:#define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */
u-boot.cfg:68:#define CONFIG_S3C2410
就在smdk2410.h
里面修改!在终端输入作弊代码:vim include/configs/smdk2410.h +21(回车键)2ehs4(Esc):wq(回车键)
然后重新编译烧写,重启开发板,发现输出信息:
U-Boot 2016.11 (Oct 12 2020 - 06:04:57 -0700)
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
DRAM: 64 MiB
WARNING: Caches not enabled
Flash: 0 Bytes
NAND: 0 MiB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: CS8900-0
Error: CS8900-0 address not set.
SMDK2410 #
成功启动!
虽然是成功启动了,我们目前改的不多,实现的功能也不多(Nand启动时没反应的),想要更好适配肯定要有更多的工作。
…
待续
arm-linux-ld -pie -T u-boot.lds -Bstatic -Ttext 0x0 $UNDEF_SYM arch/arm/cpu/arm920t/start.o
patching file arch/arm/config.mk
patching file arch/arm/cpu/arm920t/start.S
patching file arch/arm/cpu/u-boot.lds
patching file arch/arm/lib/board.c
patching file board/samsung/smdk2440/init.c
patching file board/samsung/smdk2440/lowlevel_init.S
patching file board/samsung/smdk2440/Makefile
patching file board/samsung/smdk2440/smdk2410.c
patching file boards.cfg
patching file drivers/mtd/cfi_flash.c
patching file drivers/mtd/jedec_flash.c
patching file drivers/mtd/nand/Makefile
patching file drivers/mtd/nand/nand_base.c
patching file drivers/mtd/nand/nand_util.c
patching file drivers/mtd/nand/s3c2440_nand.c
patching file include/common.h
patching file include/configs/smdk2440.h