零 补充:
vim 中跳转到函数的开头的位置:{{
跳转到函数的开头的位置 }}
一 回忆昨天的内容
qemu 的安装 gdb调试工具的使用
模拟arm核执行的状态
ARM 汇编
位运算指令
and orr eor ...
比较测试指令
cmp cmp不加s,也会影响NZCV位
tst
...
加载存储指令
ldr r0, [r1]
内存中load到寄存器 (只能是通用寄存器)
str r0, [r1]
将寄存器中的内容写入到内存中 (只能是通用寄存器)
ldmxx
fd 满栈指的是栈指针指向的内存中有数据,先改变sp的位置,在放数据
fa
ed 增栈和减栈指的是从低地址到高地址还是相反的
ea
stmxx
将数据从寄存器store进内存
局部变量的栈空间中的分布 重点掌握!
二 移植uboot,阅读uboot的源码
面试题:谈谈对uboot的理解
问:ubootpak.bin 从哪里来?
2.1 首先聊聊uboot的特点
a) uboot 属于bootloader的一种,还有armboot, vivi, redboot...
b) uboot 是著名的开源软件,相当于PC机上的bios, 德国的denx小组开发维护
http://www.denx.de
c) uboot本质上是一个裸板程序,跟咱们的shell裸板程序一样
d) uboot支持多种架构,arm,ppc, mips, dsp, fpga... 支持的开发板达上万种
e) uboot和硬件是息息相关,所以移植性较差
找开发板厂商提供源码,修改之后,匹配平台。
2.2 然后谈谈uboot的三大功能:
a) 初始化硬件的功能:
初始化硬件的目的?本质目的就是为了运行linux内核,提前准备运行环境
例如: mmc read 0x48000000 0x800 0x3000 初始化emmc mmc read
初始化cpu, 内存 .....
需要将开发板上的所有硬件都初始化么?
答:不需要,因为uboot是引导linux内核并且生命周期极短,
如果做大量的硬件的初始化,浪费时间
linux内核一旦接管,uboot做的初始化工作就无效了。
问:哪些硬件做初始化是必要的?
8大硬件
1.初始化cpu, 主要是在初始化cache缓存, 将cache中的数据指令进行
清除, 为了防止cpu一上电就去cache中拿数据,造成各种异常。
2.初始化系统时钟 sys tick IIC控制器的时钟, uart的时钟....
3.初始化内存
4.初始化闪存 EMMC
将来一旦引导起来linux内核, 中断报给linux内核。
7.初始化串口 研发阶段 发布阶段
5.初始化网卡 OTG_USB
6.关闭中断(Event), 加快系统的启动速度,
缩短启动时间,提高用户体验。
8.关闭看门狗:加快系统启动的速度,防止系统发生复位
b) 从emmc中加载linux内核到内存,并且启动linux内核
setenv bootcmd mmc read 0x48000000 0x800 0x3000 \; bootm 0x48000000
c) 给linux内核传参,告诉linux内核根文件系统在emmc的哪个分区上
setenv bootargs root=/dev/mmcblk0p2 init=/linuxrc console=ttySAC0,115200
lcd=wy070ml tp=gslx680-linux loglevel=2
2.3 谈谈uboot的源码操作
实施步骤:
a) 建议从厂商获取源码
b) 交叉编译工具
注意此代码仅仅支持芯片厂家的参考版,不一定能在你的开发板上运行起来
将来势必要根据硬件的差异修改uboot源码
c) 拷贝uboot.tar.gz 到虚拟机的porting目录下
tar xf uboot.tar.gz
cd uboot
make distclean ---> 获取做干净的源码
make x6818_config
//配置uboot源码, 配置成能够匹配abc这个参考版,只做一次
make
d) 一旦编译通过,会生成ubootpak.bin
接下来根据硬件的差异,修改uboot的源码
硬件差异怎么确定?
答:管硬件工程师要 (这是他的义务)
e) 直接上来修改uboot源码中跟硬件相关的文件
vi uboot/include/configs/x6818.h
其中包含了大量外设的硬件信息。只需要根据宏进行修改。
2.4 uboot的源码组织结构:
1. 问如何知道Uboot源码运行的入口地址?
只要知道了入口地址,然后顺藤摸瓜理清代码的执行流程,
有针对性的修改代码。
通过链接脚本,找到入口地址:
make V=1
从末尾往前找,找到连接器的链接信息。
arm-cortex_a9-linux-gnueabi-ld.bfd -pie
--gc-sections -Bstatic -Ttext 0x43C00000 -o u-boot
-T u-boot.lds arch/arm/cpu/slsiap/start.o
-Ttext 0x43C00000 :指定了uboot代码段的起始地址
-T u-boot.lds: 指定了链接时链接脚本
找到了入口点文件:
arch/arm/cpu/slsiap/s5p6818/start.S
结论:uboot代码段的第一个文件为:
arch/arm/cpu/slsiap/s5p6818/start.S
研究uboot的目录结构:
利用ctags 工具阅读分析uboot的源码:
理清uboot代码的执行流程:
1. 只关注流程,不要关注细节
2. 看注释和参数返回值等信息
怎么确定函数是否被调用:
在函数中添加打印信息
uboot代码的执行流程:
上电-> uboot -> _stext --> b reset---> bl board_init_f
--> ldr pc, =board_init_r ---> run_main_loop --> main_loop
--> cli_loop ---> cli_simple_loop ---> for(;;) //读秒,探测热键
board_init_f:
if (initcall_run_list(init_sequence_f))
发现 init_sequence_f 是一个函数指针数组
将数组中的每一个函数都调用了一遍
board_early_init_f:
db_gpio_init
nxp_rtc_init
....
hang();//如果硬件初始化异常,
直接让uboot进入死循环,不允许继续执行
board_init_r:
init_fnc_t init_sequence_r[] = {
board_init,
initr_serial,
.....
arch_early_init_r,
.....
};
uboot的代码组织结构:
见图
mmu_turn_on
物理地址,直接出现在地址总线上的值
虚拟地址,开启mmu后,由ARM核发出没有经过mmu翻译的地址
终极结论:
1. uboot中如果将来某个硬件初始化的时候发生异常,只需要在数组中找
到对应的硬件初始化函数,然后跟踪这个函数,找到问题所在
2. 如果将来在Uboot中添加一个新的硬件初始化的函数
直接在数组中添加,在可以编译到的文件中实现。
例如:
int led_init (void)
{
return 0;//成功
....
return -1;//失败
}
三 在uboot中添加logo显示
问:为何要添加logo?
商业化的行为,提升用户体验
logo本质上是一个图片
ARM处理器如何在LCD屏上显示logo图像呢?
答:首先在内存中开辟一段空间用来存储logo,
这块内存简称显存,将来内存控制器自动从显存中获取图像。
然后处理器将图像放到lcd控制器的地址总线上,LCD控制器会
自动的将图像显示到屏幕上。
logo图像在显存中如何存储?
答:图像是由一堆的像素点组成的,并且每一个像素点具有一种颜色。
每种颜色又是由RGB三种颜色组成。
X6818开发板分辨率 1024*600
颜色值在内存中的存储格式:
32位
31-------23------15----7-----0
透明度 R G B
24位
23------15-----7----0
R G B
实战演练:
向X6818开发板的LCD屏上添加LOGO显示功能
具体的实施步骤:参见《LOGO显示》