linux系列目录:
linux基础篇(一)——GCC和Makefile编译过程
linux基础篇(二)——静态和动态链接
ARM裸机篇(一)——i.MX6ULL介绍
ARM裸机篇(二)——i.MX6ULL启动过程
ARM裸机篇(三)——i.MX6ULL第一个裸机程序
ARM裸机篇(四)——重定位和地址无关码
ARM裸机篇(五)——异常和中断
linux系统移植篇(一)—— linux系统组成
linux系统移植篇(二)—— Uboot使用介绍
linux系统移植篇(三)—— Linux 内核使用介绍
linux系统移植篇(四)—— 根文件系统使用介绍
linux驱动开发篇(一)—— Linux 内核模块介绍
linux驱动开发篇(二)—— 字符设备驱动框架
linux驱动开发篇(三)—— 总线设备驱动模型
linux驱动开发篇(四)—— platform平台设备驱动
i.MX6ULL是NXP基于ARM Cortex-A7架构的单核处理器家族,主频可以高900MHz。
i.MX6ULL应用处理器包含了电源管理模块,可以降低外部电源电路设计的复杂度,并且简化了供电时序。该系列中的每个处理器都提供了丰富的内存接口,包含16-bit的LPDDR2、DDR3、DDR3L,Nand Flash、Nor Flash、eMMC、Quad SPI,还有其它广泛的接口用于连接外设的,比如WLAN、蓝牙、GPS、显示、摄像头等。
以前, ARM使用一种基于数字的命名法。在早期(1990s),还在数字后面添加字母后缀,用来
进一步明细该处理器支持的特性。就拿ARM7TDMI来说, T代表Thumb指令集, D是说支持JTAG调试(Debugging), M意指快速乘法器, I则对应一个嵌入式ICE模块。后来,这4项基本功能成了任何新产品的标配,于是就不再使用这4个后缀——相当于默许了。但是新的后缀不断加入, 包括定义存储器接口的,定义高速缓存的,以及定义“紧耦合存储器(TCM) ”的,于是形成了新一套命名法,这套
命名法也是一直在使用的。
到了架构7时代, ARM改革了一度使用的, 冗长的、 需要“解码”的数字命名法,转到另一种看起
来比较整齐的命名法。比如, ARMv7的三个款式都以Cortex作为主名。这不仅更加澄清并且“精装”
了所使用的ARM架构,也避免了新手对架构号和系列号的混淆。
ARM芯片属于精简指令集计算机(RISC:Reduced Instruction Set Computing),它所用的指令比较简单,有如下特点:
① 对内存只有读、写指令
② 对于数据的运算是在CPU内部实现
③ 使用RISC指令的CPU复杂度小一点,易于设计
比如对于a=a+b这样的算式,需要经过下面4个步骤才可以实现:
深入ARM处理器的内部。简单概括如下,我们先忽略各种CPU模式(系统模式、用户模式等等)。
CPU运行时,先去取得指令,再执行指令:
① 把内存a的值读入CPU寄存器R0
② 把内存b的值读入CPU寄存器R1
③ 把R0、R1累加,存入R0
④ 把R0的值写入内存a
由于历史原因(从ARM7TDMI开始), ARM处理器一直支持两种形式上相对独立的指令集, 它
们分别是:
这两种指令集也对应了两种处理器执行状态。在程序的执行过程中,处理器可以动态地在两种执行状态之中切换。 实际上, Thumb指令集在功能上是ARM指令集的一个子集,但它能带来更高的代码密度,给目标代码减肥。这对于要勒紧裤腰带的应用还是很经济的。
Thumb-2是Thumb的超集,它支持both 16位和32位指令。
ARM体系结构是一种基于模式的体系结构。在引入安全扩展之前,它具有7种处理器模式,如上表所示。有六个特权模式和一个非特权用户模式。特权是执行用户(非特权)模式无法完成的某些任务的能力。在用户模式下,对影响整个系统配置的操作存在一些限制,例如,MMU的配置和缓存操作。
模式与异常事件相关,可以这样简单理解:
① 板子上电时,CPU处于SVC模式,它用的是SVC模式下的寄存器
② 程序运行时发生了中断,CPU进入IRQ模式,它用的IRQ模式下的寄存器
③ CPU处理完中断,它切换回SVC模式,继续使用SVC模式下的寄存器
④ CPU发生某种异常时,比如读取内存出错,它会进入ABT模式,使用ABT模式下的寄存器来处理错误。
在某种模式下,CPU执行时使用的是这种模式的资源,比如使用的是这组模式的寄存器。
这样就可以免去保存上一个模式所使用的寄存器。
ARM体系结构提供了十六个32位通用寄存器(R0-R15)供软件使用。其中的15个(R0-R14)可用于通用数据存储,而R15是程序计数器,其值随处理器执行指令而改变。显式地写入R15可以更改程序流程。软件还可以访问CPSR和SPSR。SPSR中保存的上一个运行模式的CPSR的副本。
同一个寄存器可能在不同模式下对应物理上不同的位置。只有特定模式下才能访问到这些位置的寄存器。
有些寄存器,不同的工作模式下有自己的副本,当切换到另一个工作模式时,那个工作模式的寄存器副本将被使用,这些寄存器被称为备份寄存器。备份寄存器在物理上使用不同的存储,通常仅在特定模式下才可以访问它们。下图中带阴影标记的寄存器都是备份寄存器。
R13(在所有模式下)是堆栈指针,但是当堆栈操作不需要时,它可以用作通用寄存器。
R14(链接寄存器)保存BL分支指令的下一条指令的地址。当它不支持子程序的返回时,它也可以用作通用寄存器。R14_svc,R14_irq,R14_fiq,R14_abt和R14_und同样用于在发生中断和异常时,或者执行转移和链接指令时,备份R15的返回值。
R15是程序计数器并保存当前程序地址(实际上,在ARM状态下,它始终指向当前指令之后的八个字节,而在Thumb状态下,它始终指向当前指令之后的四个字节,这是原始ARM1的三级流水线的遗留特性)。在ARM状态下读取R15时,位[1:0]为零,位[31:2]包含PC值。在Thumb状态下,位[0]始终读为零。
R0-R14的复位值是不定的。在使用堆栈之前,必须通过引导代码初始化SP(堆栈指针)。因为每种模式下的R13即SP寄存器都有自己的实体,所以你用到哪种模式,就需要单独为该模式设置SP。ARM体系结构过程调用标准(AAPCS)或ARM嵌入式ABI(AEABI)指定了软件应如何使用通用寄存器,以便在不同的工具链或编程语言之间进行互操作。
程序状态寄存器(CPSR,current programmer status register)包含处理器的状态和一些控制标记位。
N,bit[31]负数标记位
Z,bit[30]零标记位
C,bit[29]进位标记位
V,bit[28]溢出标记位
这些条件标记位可以在任何模式下读写。
GE[3:0],bit[19:16]一些SIMD指令使用
IT[7:2],bit[15:10]Thumb2指令集的If-then条件指令使用
J, bit[24] 处理器是否处于Jazelle状态,和T, bit[5]一起决定执行的指令集
E, bit[9] 大小端状态位,0表示小端,1表示大端。
Mask bits, bits[8:6]
A, bit[8] 异步中止禁止位Asynchronous abort mask bit.
I, bit[7] IRQ 禁止位
F, bit[6] FIQ 禁止位
Q,bit[27]为1的话,表明执行一些指令时出现饱和或者溢出,一般与DSP有关。
T, bit[5] Thumb指令位,和J,bit[24]位决定了处理器的指令集,ARM,Thumb,Jazzelle或者ThumbEE
M[4:0], bits[4:0]工作模式位,决定了处理器当前处于的工作模式。
处理器可以使用直接写入CPSR模式位来实现模式之间切换。更常见的是,处理器会由于异常事件而自动切换模式。在用户模式下,无法改变处理器模式的PSR位[4:0]来切换模式和A,I和F位来使能或者禁止异步中止、IRQ和FIQ。
协处理器,顾名思义它也是一个处理器,不过它是为了“协助”主处理器而存在的。ARM架构里有16个协处理器,CP0~CP15。编程时涉及CP15比较多。
CP15 协处理器一般用于存储系统管理, 但是在中断中也会使用到, CP15 协处理器一共有16 个 32 位寄存器。 CP15 协处理器的访问通过如下另个指令完成:
MRC: 将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中。
MCR: 将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中。
MRC 就是读 CP15 寄存器, MCR 就是写 CP15 寄存器, MCR 指令格式如下:
MCR{cond} p15, < opc1 >, < Rt >, < CRn >, < CRm >, < opc2 >
cond:指令执行的条件码,如果忽略的话就表示无条件执行。
opc1:协处理器要执行的操作码。
Rt: ARM 源寄存器,要写入到 CP15 寄存器的数据就保存在此寄存器中。
CRn: CP15 协处理器的目标寄存器。
CRm: 协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm 设置为 C0,否则结果不可预测。
opc2: 可选的协处理器特定操作码,当不需要的时候要设置为 0。
MRC 的指令格式和 MCR 一样,只不过在 MRC 指令中 Rt 就是目标寄存器,也就是从CP15 指定寄存器读出来的数据会保存在 Rt 中。而 CRn 就是源寄存器,也就是要读取的写处理器寄存器。
MRC 的指令格式和 MCR 一样,只不过在 MRC 指令中 Rt 就是目标寄存器,也就是从CP15 指定寄存器读出来的数据会保存在 Rt 中。而 CRn 就是源寄存器,也就是要读取的写处理器寄存器。
假如我们要将 CP15 中 C0 寄存器的值读取到 R0 寄存器中,那么就可以使用如下命令:
MRC p15, 0, r0, c0, c0, 0
c1 寄存器
c1 寄存器同样通过不同的配置,其代表的含义也不同,如图:
当 MRC/MCR 指令中的 CRn=c1, opc1=0, CRm=c0, opc2=0 的时候就表示此时的 c1 就是 SCTLR 寄存器,也就是系统控制寄存器,这个是 c1 的基本作用。 SCTLR 寄存器主要是完成控制功能的,比如使能或者禁止 MMU、 I/D Cache 等。
bit13: V , 中断向量表基地址选择位,为 0 的话中断向量表基地址为 0X00000000,软件可
以使用 VBAR 来重映射此基地址,也就是中断向量表重定位。为 1 的话中断向量表基地址为
0XFFFF0000,此基地址不能被重映射。
bit12: I, I Cache 使能位,为 0 的话关闭 I Cache,为 1 的话使能 I Cache。
bit11: Z,分支预测使能位,如果开启 MMU 的话,此位也会使能。
bit10: SW, SWP 和 SWPB 使能位,当为 0 的话关闭 SWP 和 SWPB 指令,当为 1 的时候就使能 SWP 和 SWPB 指令。
bit9:3:未使用,保留。
bit2: C, D Cache 和缓存一致性使能位,为 0 的时候禁止 D Cache 和缓存一致性,为 1 时使能。
bit1: A,内存对齐检查使能位,为 0 的时候关闭内存对齐检查,为 1 的时候使能内存对齐检查。
bit0: M, MMU 使能位,为 0 的时候禁止 MMU,为 1 的时候使能 MMU。
c12 寄存器
c12 寄存器通过不同的配置,其代表的含义也不同,如图:
在图 中当 MRC/MCR 指令中的 CRn=c12, opc1=0, CRm=c0, opc2=0 的时候就表示此时 c12 为 VBAR 寄存器,也就是向量表基地址寄存器,在讲解到中断的时候,会用到此寄存器。
设置中断向量表偏移的时候就需要将新的中断向量表基地址写入 VBAR 中,假设代码链接的起始地址为0X87800000 ,而中断向量表肯定要放到最前面,也就是 0X87800000 这个地址处。所以就需要设置 VBAR 为 0X87800000,设置命令如下:
ldr r0, =0X87800000 ; r0=0X87800000
MCR p15, 0, r0, c12, c0, 0 ;将 r0 里面的数据写入到 c12 中,即 c12=0X87800000
c15 寄存器
c15 寄存器也可以通过不同的配置得到不同的含义:
GIC 的基地址就保存在 CBAR中,我们可以通过如下命令获取到 GIC 基地址:
MRC p15, 4, r1, c15, c0, 0 ; 获取 GIC 基础地址,基地址保存在 r1 中
获取到 GIC 基地址以后就可以设置 GIC 相关寄存器了,GIC是中断控制块,在讲解到中断的时候,会用到此寄存器。
总结:
通过配置协处理器的 c1 寄存器可以使能或禁止 MMU、 I/D Cache 等;通过 c12 寄存器可以设置中断向量偏移;通过 c15 寄存器可以获取 GIC 基地址。