经过前两节的准备,我们现在可以开始肝u-boot的代码了
U-boot版本:2020/5/2
编译环境:Ubuntu 16.04
arm-none-eabi-gcc version 4.9.3 20150529 (prerelease) (15:4.9.3+svn231177-1)
运行环境:mini2440(s3c2440,arm920t)
代码仓库:git@github.com:JingyeLi/u-boot_2440.git
https://github.com/JingyeLi/u-boot_2440/tree/v0.1
u-boot.lds
这是一个很容易被人忽略的一个文件,包括我自己,以前一般用keil的时候都是自动生成的,但其实想要理解一个芯片的启动代码,最好先从lds文件开始(不过在此之前,希望你能对编译-链接-运行有所理解)。众所周知一个程序会分成对应的几段,而lds文件的用途就是规定好这几段会放在什么地方。
而lds的重点是开头的这几句,与程序入口密切相关,程序虽然还是从_start开始,但与老版的u-boot不同,新版的多了__image_copy_start和vectors,__image_copy_start我们放在后面relocate的时候一起理解,这里可以先忽略。
ENTRY(_start) /* 表明可执行文件代码从_start开始 */
SECTIONS
{
. = 0x00000000; /* 指定这个段从0x0开始*/
. = ALIGN(4); /* 4字节对齐,32位指令* /
.text : /* 具体TEXT段的结构 */
{
*(.__image_copy_start)
*(.vectors)
CPUDIR/start.o (.text*)
}
}
lib/vectors.S
在芯片启动后,会自动执行load 0x0的指令,而在u-boot.lds中,我们知道0x0的位置对应的指令应该就是_start标签所在的地方。找了一圈发现,新版的u-boot将arm的中断向量表都放在lib/vectors.S中。这个文件其实也简单,十分的好理解,找到_start然后发现就是跳到reset
.macro ARM_VECTORS
b reset /* 这个指令将会保存在_start的地址 */
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
.endm
_start:
ARM_VECTORS
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
S3C2440初始化 - start.S/lowlevel_init.S
U-boot在0x0会跳转到’reset’ label,而这个label对应在arch/arm/cpu/arm920t的start.S中。
具体代码的分析就不贴了,基本和旧的u-boot版本没什么区别。这里推荐一篇blog,虽然他用到的u-boot版本还是很旧的,但是确实讲得很清楚很好。
https://blog.csdn.net/qq_16933601/article/details/105685770
CPU的初始化分为几步,
设置为特权模式(SVC32)
关闭看门狗
关闭中断
关闭MMU和cache
初始化时钟
初始化SRAM
https://blog.csdn.net/dndxhej/category_9261234.html?spm=1001.2014.3001.5482
不过初始化时钟值得仔细讲一下,一开始移植的时候,执行到某条跳转指令程序就跑飞了,出现data abort或者unhandled exception,百思不得其解,后来就是看到上述那篇博文,找到了思路。
首先我们应该理解ARM的时钟系统。
S3C2440有两个PLL,MPLL用于提供系统时钟,UPLL用于USB模块,而在编程手册上提到一点。
Although the MPLL starts just after a reset, the MPLL output (Mpll) is not used as the system clock until the software
writes valid settings to the MPLLCON register. Before this valid setting, the clock from external crystal or EXTCLK source
will be used as the system clock directly. Even if the user does not want to change the default value of MPLLCON
register, the user should write the same value into MPLLCON register.
对于频率的计算方法,在编程手册上也有,
MPLL = (2 * m * Fin) / (p * 2^s)
m = M (the value for divider M)+ 8, p = P (the value for divider P) + 2
同时还有一个建议,建议最终的频率大于200MHz
FCLKOUT must be bigger than 200MHz (It does not mean that the ARM core has to run more than 200MHz).
而和时钟设置相关的两个寄存器分别是CLKDIVN和MPLLCON
因为刚reset的时候MPLLCON是不生效的,FCLK直接使用晶振时钟,同时manual的下面还有推荐的频率,这里我们就设置MPLL为400MHz方便计算,下面NOTE还提示到要有7个NOP时钟才能生效
MPLLCON = ((0x5c << 12) | (0x01 << 4) | (0x01))
同时CLKDIVN设置为0x5,则得到
FCLK = 400MHz
HCLK = 100MHz
PCLK = 50MHz
所以我们能得到以下初始化时钟代码加在start.S里面:
# define MPLLCON 0x4C000004
# define CLKDIVN 0x4C000014 /* clock divisor register */
# define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
/* default FCLK is 12 MHz ! */
ldr r0, =CLKDIVN
mov r1, #0x03 // FCLK:HCLK:PCLK=1:2:4
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */
mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */
/* MPLLCON = S3C2440_MPLL_400MHZ */
ldr r0, =MPLLCON
ldr r1, =S3C2440_MPLL_400MHZ
str r1, [r0]
在控制时钟寄存器设置好后,接下来的初始化步骤就是按上述所讲,就不再多解释了。也可以理解为,当程序执行到_main(start.S的最后一步),CPU就初始化好了,可以正常执行所有功能。
附 uboot调试方法
在bringup CPU的时候,其实出现的问题远比记录在博客上的多,收集了两个在串口没好前的调试方法
AXD
由于uboot是使用命令行Makefile直接编译的,没有使用Keil,不能直接使用JTAG的单步调试,这时我们可以借用AXD,参考:
https://blog.csdn.net/xautfengzi/article/details/6306323
OpenOCD
这是一个功能比JLink更强大的工具
https://blog.csdn.net/qingwufeiyang12346/article/details/45954595
说一下自己的感想,其实自己作为嵌入式工程师工作也有好几年了,突然想搞u-boot的移植并且用博客记录下来,是出于自己的初心,在技术道路上,就是要无限进步。现在网上搜索,都2021年了,大部分人移植还拿着200x年的u-boot在捣鼓,能参考的资料多是没错,但是计算机技术从来就不是守旧主义,我们要理解的是一颗芯片如何bringup,如何从一条条的机器指令到整个系统跑起来,我们要提供什么样的代码,初始化的思路是什么。u-boot作为一个经典的底层开源项目,我们从中能吸取到什么。在招聘面试中,要问的从来不是做了多少项目,而是问怎么去做这个项目。
上一篇:移植uboot到mini2440(一)
下一篇:【嵌入式】从零开始移植U-boot到mini2440(四)——C runtime配置篇
推荐阅读最新更新时间:2024-11-13 10:24
设计资源 培训 开发板 精华推荐
- 使用 ROHM Semiconductor 的 BA33BC0WT 的参考设计
- LT3510EFE 演示板、双路单片式降压型 DC/DC 转换器
- ESPboy 梅子1.0
- MIC2098-2YMT限流配电开关典型应用
- LTC3615IUF-1 双路 3A 同步降压型 DC/DC 转换器用于 DDR 存储器终端的典型应用
- 使用 ROHM Semiconductor 的 BD49L33G-TL 的参考设计
- LTC2241CUP-10 演示板,CMOS 输出,VCC = 2.5V,210Msps,10 位,10MHz< AIN< 250MHz
- 使用 Analog Devices 的 LTC3130EMSE-1 的参考设计
- LT1021CCN8-10 精密 DAC 电压基准的典型应用
- 2kW全数字开关模式交流-直流转换器评估板