注意上表中,0x4000_0000~0xA000_0000,0xA000_0000~0x0000_0000 这两个地址
区间,这两个区间是 DMC 内存控制器的寻址地址,也就是内存的物理地址。实际上 4412 最
大支持的内存可以达到 3G,32 位处理器理论上可以支持 2 的 32 次方(最大 4G),如上表
所示,其中 1G 的地址给了 iROM、iRAM 等等这些 MPU 内部寄存器使用,所以 32 位 MPU
是不可能达到 4G 内存的。
现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要
MMU(Memory Management Unit)的支持。MMU 通常是 CPU 的一部分,如果处理器
没有 MMU,或者有 MMU 但没有启用,CPU 执行单元发出的内存地址将直接传到芯片引脚
上,被内存芯片(物理内存)接收,这称为物理地址(Physical Address),如果处理器启用
了 MMU,CPU 执行单元发出的内存地址将被 MMU 截获,从 CPU 到 MMU 的地址称为虚拟
地址(Virtual Address),而 MMU 将这个地址翻译成另一个地址发到 CPU 芯片的外部地址
引脚上,也就是将虚拟地址映射成物理地址。通过内存管理单元,可以实现 4G 的虚拟内存。
在 uboot 代码中,需要多次用到以上地址的概念,其中内存管理单元被开启或者关闭,
所以有必要先介绍一下这几个地址的概念。
3.1.2 关于汇编语法
如果学习过单片机课程,会发现大部分都是使用 C 语言去编码,汇编使用的非常少了。
那么还有必要去学习汇编么?其实是没有必要的,因为在 uboot 中汇编代码量非常少,以
4412 的 uboot 源码为例,其中有效的汇编代码不足 200 行,我们根本不需要为了读懂 200
行代码专门去学习一门编程语言。
作者这里建议,首先我们的目标是一定要把这些代码读明白,如果不明白会影响后面 C
代码的阅读,以及 uboot 的移植;其次,我们要弄清楚每一行有效汇编代码的语法。
现在我们已经知道汇编是从“cpu/arm_cortexa9/start.S”这个文件开始执行,那么我们
就从第一行代码的语法开始学习,代码执行到或者跳到哪一行,我们就学习这一行代码的语
法。
在手册的附录部分,我们会依次介绍汇编代码中出现的语法,大家也可以通过互联网学习
每一行执行的汇编语法。
3.1.3 uboot 汇编代码初始化串口之前的简易调试方法
在前面教程中我们介绍过,从 A9 开始,
开发板一般都不配 jtag,jtag 价格昂贵,在 A9
之前,由于引导程序 uboot 必须通过 jtag 来烧写,但是在 A9 处理器上,大部分都是支持 tf
卡引导,这样可以免去 jtag 的费用,烧写变的简单高效。
那么没有 jtag,对于 uboot 的调试,我们没法单步调试,如果有一行代码我们不是很确
定到底执行了没,或者跳到哪一行。如果代码已经执行到串口初始化阶段,当然是可以通过串
口打印字符来实现,在串口初始化之前,其实可以通过控制 LED 灯来跟踪代码。
以下是开发板上两个小灯控制的代码,可以将小灯点亮。
点亮 LED2 灯:ldr r0, =0x11000104 /* GPL2(0) */
ldr r1, =0x00000001 /* GPL2(0 output high) */
str r1, [r0]
ldr r0, =0x11000100 /* GPL2(0) */
ldr r1, =0x00000001 /* GPL2(0 output high) */
str r1, [r0]
点亮 LED3:
ldr r0, =0x11000060
ldr r1, =0x00000010
str r1, [r0]
ldr r0, =0x11000064
ldr r1, =0x00000002
str r1, [r0]
这里简单介绍下这几行汇编代码的含义。
ldr r0, =0x11000104
ldr 是将 0x11000104 值赋给 r0 寄存器。这个值地址为 GPL2DAT。
ldr r1, =0x00000001
ldr 是取 0x11000104 地址的值赋给 r1 寄存器。
str r1, [r0]
str 是将 r1 的值写入到 r0 数值对应物理地址寄存器中。将 0x00000001 写入到
0x11000104 地址寄存器中,0x11000104 地址是 GPL2DAT 寄存器。
ldr r0, =0x11000100 /* GPL2(0) */
ldr r1, =0x00000001 /* GPL2(0 output high) */
str r1, [r0]
将 0x00000001 写入到 0x11000100 地址寄存器中,0x11000100 地址是 GPL2CON 寄
存器。执行这两步就可以将 LED2 点亮。
点亮 LED3 和点亮 LED2 类似。
在串口初始化之前可以通过点灯来实现调试,串口初始化之后可以通过打印字符来跟踪调
试代码。