1.基础知识
内核版本号 | Soc版本号 | 芯片型号(三星) |
---|---|---|
ARMv1 | ||
… | ||
ARMv4 | ARM7 | S3C44B0 |
ARMv4 | ARM9 | S3C2440 S3C2410 |
ARMv5 | ARM9+xScale | |
ARMv6 | ARM11 | S3C6410 |
ARMv7 | Cortex-M Microcontroller,单片机使用 Cortex-A application 手机,平板电脑使用 Cortex-R real time,实时操作系统 |
Soc :System on chip (将一部分外设和CPU集成的芯片)
外设(Peripherial) : 外部设备(除了CPU以外的设备)
BSP :board support package 板级支持包(支持硬件的相关软件,官方给的硬件包/驱动,根据板子移植过的)
地址总线和数据总线
CPU通过地址总线寻址,然后通过数据总线与外部设备互换信息,地址总线的位数决定CPU的寻址范围;数据总线的位数决定CPU单次通信能交换的信息数量。总线的速度决定CPU和外设互换信息的速度。CPU的地址总线位数和数据总线可以不同,但一般都相同,CPU的位数指的是数据总线的位数,32位CPU寻址范围是4G,所以最多支持4G内存,数据总线是32位的,所以内存32位的好,所以编程最好用int
可编程逻辑器件 英文全称为:programmable logic device 即 PLD。
对于可编程逻辑器件,设计人员可利用价格低廉的软件工具快速开发、仿真和测试其设计。 然后,可快速将设计编程到器件中,并立即在实际运行的电路中对设计进行测试。
CISC | RISC |
---|---|
complex instruction set computer复杂指令集CPU | Reduced Instruction-Set Computer精简指令 |
用最少的指令来完成任务 设计复杂、工艺复杂 编译器好设计 | 让软件来完成具体的任务,CPU本身仅提供基本功能指令集 CPU的设计和工艺简单 编译器的设计变难 |
内存 | IO | ||
---|---|---|---|
定义 | 程序的运行场所,和CPU之间通过总线连接,内存通过CPU的地址总线来寻址定位,然后通过CPU数据总线来读写。 | 输入输出接口,是CPU和其他外部设备之间通信的道路。一般的,IO就是指CPU的各种内部或外部外设。 | |
与CPU的连接方式 | 直接连接 | IO与内存统一编址方式 : 类似于访问内存的方式,即把外设的寄存器当作一个内存地址来读写,从而以访问内存相同的方式来操作外设 | IO与内存独立编址 : 使用专用的CPU指令来访问 某种特定外设 |
优点 | 效率高访问快 | IO当作内存来访问,编程简单 | 不占用CPU地址空间 |
缺点 | 资源有限,扩展性差。 | IO也需要占用一定的CPU地址空间,而CPU的地址空间是有限资源。 | CPU设计变复杂了 |
冯诺依曼结构 | 哈佛结构 | |
---|---|---|
定义 | 程序和数据都放在内存中,且不彼此分离 | 程序和数据分开独立放在不同的内存块中,彼此完全分离 |
优点 | 处理起来简单 | 安全和稳定性高 |
缺点 | 安全和稳定性低 | 软件处理复杂一些 |
寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果, 是CPU的硬件设计者制定的,目的是留作外设被编程控制的“ 活动开关 ”,正如汇编指令集是CPU的编程接口API一样,寄存器是外设硬件的软件编程接口API。使用软件编程控制某一硬件,其实就是编程读写该硬件的寄存器。
编程操作寄存器类似于访问内存,每个bit位都有特定含义,因此编程操作时需要位操作。单个寄存器的位宽一般和CPU的位宽一样,以实现最佳访问效率。
SoC中有2类寄存器:
• 通用寄存器(ARM中有37个)是CPU的组成部分,CPU的很多活动都需要通用寄存器的支持和参与。
• SFR(special function register,特殊功能寄存器)不在CPU中,而存在于CPU的外设中,我们通过访问外设的SFR来编程操控这个外设,这就是硬件编程控制的方法。
ARM体系结构
- ARM是RISC架构 常用ARM汇编指令只有二三十条,是低功耗CPU
- SoC中的各种内部外设通过各自的SFR编程访问,这些SFR的访问方式类似于访问普通内存,这叫IO与内存统一编址。
- ARM是哈佛结构的,保证了ARM CPU运行的稳定性和安全性,因此ARM适用于嵌入式领域,哈佛结构也决定了ARM裸机程序(使用实地址即物理地址)的链接比较麻烦,必须使用复杂的链接脚本告知链接器如何组织程序;对于OS之上的应用(工作在虚拟地址之中)则不需考虑这么多
存储设备 | 含义 |
---|---|
ROM | 只读存储器(read only memory),比如硬盘,flash。这里的不能写指的是CPU不能通过地址总线和数据总线去写。硬盘的写是通过专用的IO接口写。 |
RAM | 随机存取存储器(random access memory),可以随机访问,想访问哪个地址就访问哪个地址,存储器当系统断电后,数据会丢失。 flash是顺序访问,按页访问,要访问第四个字节需要从头开始,0,1,2,3,访问完才能访问4 |
IROM | internal rom 内部ROM,指的是集成到SoC内部的ROM |
IRAM | internal ram 内部RAM,指的是集成到SoC内部的RAM |
DRAM | 外部动态随机存储器,需要每隔一段时间就刷新充电一次,否则的话数据会丢失。DRAM的速度比SRAM要慢,但是比ROM快,速度慢那么对应的价格也要比SRAM便宜一些。DRAM分为很多种,常见的有SDRAM,DDRRAM,RDRAM,SGRAM等等 |
SRAM | 静态随机存储器,它具有静止存取的功能,也就是可以不需要刷新电路就保存内部的数据。性能高,功耗小,速度快,价格高,它是目前(2017年)最快的存储设备。 |
SROMC | SROM Controler |
ONENAND | 三星发明的一种NAND |
NAND | NAND flash |
SFR | special function register 特殊功能寄存器 |
rom最初不能编程,出场什么内容就永远什么内容,不灵活。后来出现了prom,可以自己写入一次,要是写错了,只能换一片。随着不断的进步,出现了可多次擦除写入的EPROM,每次擦除要把芯片拿到紫外线上照一下,想一下你往单片机上下了一个程序之后发现有个地方需要加一句话,为此你要把单片机放紫外灯下照半小时,然后才能再下一次,这么折腾一天也改不了几次。历史的车轮不断前进,伟大的EEPROM出现了,拯救了一大批程序员,终于可以随意的修改rom里的内容了。
EEPROM全称是“电可擦除可编程只读存储器”,即Electrically Reasable Programmable Read-Only Memory,是相对与紫外擦除的rom来讲的,但是今天已经存在多种EEPROM的变种,变成了一类存储器的统称。
侠义的EEPROM:这种rom的特点是可以随机方位和修改任何一个字节,可以往每个bit中写入0或者1,这是传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100W次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。
flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了去吧于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。
flash做的改进就是擦除时不再以字节为单位而是以块为单位,一次简化了电路,数据密度更高,降低了成本。上M的rom一般都是flash.
flash一般分为norflash和nand flash,nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节,但是擦除仍要按块来擦。
nand flash 同样时是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址,读取只能按页来读取。
由于nand flash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的,小容量的2~12M的flash多是nor型的
使用寿命上,nand flash的擦除次数是nor的数倍,而且nand flash可以标记坏块,从而是软件跳过坏块,nor flash一旦损坏便无法使用。
因为nor flash可以进行字节寻址,所以程序可以再nor flash中运行,嵌入式系统多是用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。
ROM是放代码的,RAM是放数据的,IROM和IRAM之前有一段不能用的空间是防止越界,覆盖原先的内容。
安全区域:CPU是否对代码做校验
内存(RAM) | 外存(ROM) | |
---|---|---|
定义 | 内部存储器 | 外部存储器 |
作用 | 用来运行程序的 | 用来存储东西的 |
例子 | DRAM SRAM DDR 地址总线链接 | 硬盘 flash(Nand iNand U盘 SSD) 光盘 |
与CPU连接方式 | 地址总线&数据总线的总线式访问方式连接,好处是直接访问,随机访问;坏处是占用CPU的地址空间,大小受限 | 通过CPU的外存接口来连接的,好处是不占用CPU的地址空间,坏处是访问速度没有总线式快,访问时序较复杂 |
Soc常用外存:
flash(电子存储,嵌入式) | NorFlash | 总线式访问,可以接到地址空间,接到 SROMbank,一般用来启动,小而贵 | |
NandFlash(通过时序访问) | NandFlash | 分为SLC(容量小,价格高,时序简单,不容易坏块)和MLC(容量大,价格低,时序复杂,容易坏块) | |
eMMC/iNand/moviNand | eMMC(embeded MMC),iNand是sanDisk公司出产的eMMC,moviNand是三星公司出产的eMMC | ||
oneNAND | oneNand是三星公司出的一种Nand | ||
SD卡/TF卡/MMC卡 | 其实都是SD卡 | ||
eSSD | embeded SSD,固态硬盘 | ||
硬盘(磁存储,台式机) | SATA硬盘 | 机械式访问、磁存储原理、SATA指的是接口,价格便宜 |
x210有2个版本,Nand版和iNand版,分别使用Nandflash和iNand作为外部存储器,我们使用的是iNand版本,板载4GB iNand。
S5PV210共支持4个SD/MMC通道,其中通道0和2依次用作启动。X210开发板中SD/MMC0通道用于连接板载MMC,因此外部启动时只能使用SD/MMC2通道。
2.S5PV210的启动过程详解
特点 | 优缺点 | |||
内存 | SRAM | 静态内存 | 容量小、价格高,基本没有上兆(M)的 | 优点是不需要软件初始化,直接上电就能用 |
DRAM | 动态内存 | 容量大,价格低 | 缺点是上电后不能直接使用,需要软件初始化后才能使用 | |
单片机中:内存需求量小,而且希望开发尽量简单,适合全部用SRAM 嵌入式系统:内存需求量大,而且没有NorFlash等可启动介质 PC机: 内存需求量大,而且软件复杂,不在乎DRAM的初始化开销,适合全部用DRAM | ||||
外存 | NorFlash | 容量小,价格高 | 优点是可以和CPU直接总线式访问,CPU上电后可以直接读取,所以一般用作启动介质 | |
NandFlash | 和硬盘一样 | 容量大,价格低 | 缺点是不能总线式访问,也就是说不能上电CPU直接读取,需要CPU先运行一些初始化软件,然后通过时序接口读写。 | |
一般PC机都是很小容量的BIOS(NorFlash) + 很大容量的硬盘(类似于NandFlahs)+ 大容量的DRAM 嵌入式系统:因为NorFlash很贵,所以现在很多嵌入式系统倾向于不用NorFlash,直接用外接的大容量Nand + 外接大容量DRAM + Soc内置SRAM 单片机: 很小容量的NorFlah + 很小容量的SRAM |
- CPU上电后先从内部IROM中读取预置的代码(BL0),执行。这段IROM代码首先做了一些基本的初始化(CPU时钟、关看门狗等Soc内部的初始化);
- iROM引导代码将第一部分启动代码(BL1,最大为16KB)加载到内部SRAM中。
- 通过BL1加载BL2(最大位80KB)
- BL2初始化DRAM控制器,然后加载OS到SDRAM
- 跳转到OS起始地址,初始化OS运行环境并运行
BL0工作:
- 关看门狗
- 初始化指令cache
- 初始化栈
- 初始化堆
- 初始化块设备复制函数device copy function
- 初始化PLL,设置SoC时钟系统
- 复制BL1到内部IRAM(16KB)
- 检查BL1的校验和
- 跳转到BL1去执行
SoC通过OMpin来识别外部启动介质,目前有下面三种方式启动:
- 使用板载的eMMC启动,接在SD0,启动设置为101100
(收到的开发版本默认就是从eMMC启动,内部预先烧录了Android) - 使用外置SD卡从SD2通道启动,OMpin设置和SD0启动一样,但这需要先破坏板载的eMMC中的android镜像
(不管OMpin如何设置,Second启动一定时从通道2上启动) - USB调试模式设置为1xxxx1
拨码开关设置我们只需动OM5即可,其他几个根本不需要碰。需要SD启动时OM5打到GND,需要USB启动时OM5打到VCC
3.ARM编程
ARM 采用的是32位架构.
ARM 约定:
- Byte : 8 bits
- Halfword :16 bits (2 byte)
- Word : 32 bits (4 byte)
基本工作模式 | 定义 | 应用 | |
用户模式 | User | 非特权模式,大部分任务执行在这种模式 | 用户态应用进程 |
异常模式 | FIQ | 当一个高优先级(fast) 中断产生时将会进入这种模式 | 中断 |
IRQ | 当一个低优先级(normal) 中断产生时将会进入这种模式 | ||
Supervisor | 当复位或软中断指令执行时将会进入这种模式 | bootloader,引导内核 | |
Abort | 当存取异常时将会进入这种模式 | 异常中的异常 | |
Undef | 当执行未定义指令时会进入这种模式 | ||
系统模式 | System | 使用和User模式相同寄存器集的特权模式 | 内核专用 |
CPU为什么设计这些模式?
- CPU是硬件,OS是软件,软件的设计要依赖硬件的特性,硬件的设计要考虑软件需要,便于实现软件特性。
- 操作系统有安全级别要求,因此CPU设计多种模式是为了方便操作系统的多种角色安全等级需要。
37个寄存器
System模式使用user模式寄存器集
寄存器说明:
sp :stack pointer,堆栈指针寄存器,始终指向栈顶
lr : Link Register,连接寄存器,用途有两种,一是用来保存子程序返回地址;二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2),因此在各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行。
pc :Program control register 程序控制寄存器,PC指向哪里,CPU就会执行哪条指令(所以程序跳转时就是把目标地址代码放到PC中)
cpsr : Current Program Status Register,程序状态寄存器
条件位:(ALU 算术逻辑单元)
- N = Negative result from ALU (负数)
- Z = Zero result from ALU (结果为0)
- C= ALU operation Carried out(有进位)
- V = ALU operation oVerflowed(溢出)
Q 位:(一般不用)
- 仅ARM 5TE/J架构支持
- 指示饱和状态
J 位(一般不用)
- 仅ARM 5TE/J架构支持
- J = 1: 处理器处于Jazelle状态
中断禁止位:
- I = 1: 禁止 IRQ.
- F = 1: 禁止 FIQ.
T Bit
- 仅ARM xT架构支持
- T = 0: 处理器处于 ARM 状态
- T = 1: 处理器处于 Thumb 状态
Mode位:
- 处理器工作模式位
spsr : Saved Program Status Register 程序状态保存寄存器,用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。SPSR用来进行异常处理,有以下功能:
1.保存ALU中的当前操作信息。
2.控制允许和禁止中断。
3.设置处理器的运行模式。
ARM的异常处理机制:
当异常产生时, ARM core
- 拷贝 CPSR 到 SPSR_<mode>
- 设置适当的 CPSR 位:
改变处理器状态进入 ARM 态
改变处理器模式进入相应的异常模式
设置中断禁止位禁止相应中断 (如果需要) - 保存返回地址到 LR_<mode>
- 设置 PC 为相应的异常向量
返回时, 异常处理需要:
- 从 SPSR_<mode>恢复CPSR
- 从LR_<mode>恢复PC
- Note:这些操作只能在 ARM 态执行.
4.ARM 汇编指令集
- 汇编指令是CPU指令的助记符,经过编译后得到一串01组成的机器码,可以由CPU读取执行
- 汇编伪指令本质上不是指令,它是编译环境提供的,目的是为了指导编译环境,经过编译后伪指令不会生成机器码。
两种不同风格的ARM指令
- ARM官方汇编风格,windows IDE中常用,例如:LDR R0,[R1]
- GNU汇编风格,LINUX中常用,例如:ldr r0,[r1]
ARM汇编的特点:
- ARM采用RISC架构,CPU本身不能直接读取内存,需要先将内存中的内容加载入CPU的通用寄存器,然后才能被CPU处理
ldr (load rigister) : 将内存内容加载入通用寄存器
str (store rigister) : 将寄存器内容存入内存空间 - 8种寻址方式
寻址方式 | 例子 | 解释 |
---|---|---|
寄存器寻址 | mov r1, r2 | 把寄存器r2的值赋值给寄存器r1 |
立即寻址 | mov r0, #0xFF00 | 立即数0xFF00赋值给寄存器r1 |
寄存器移位寻址 | mov r0, r1, lsl #3 | r1逻辑左移3位,结果放入r0 |
寄存器间接寻址 | ldr r1, [r2] | 把r2的值当成地址,取出相应值,赋给r1 |
基址变址寻址 | ldr r1, [r2, #4] | r2中的值加上4,从这个地址取出值赋给r1 |
多寄存器寻址 | ldmia r1!, {r2-r7, r12} | 将r1指向的地址上连续空间的数据,保存到r2-r7,r12当中,ia后缀表示按WORD递增 |
堆栈寻址 | stmfd sp!, {r2-r7, lr} | 现场保存,将r2~r7,lr入栈 |
相对寻址 | beq flag | 相等跳转 |
-
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
1) B(byte) 功能不变,操作长度变为8位
2) H(half word) 功能不变,操作长度变为16位
3) S(signed) 功能不变,操作数变为有符号数
例如:ldr ldrb(加载8位) ldrh(加载16位) ldrsb(加载有符号数8位) ldrsh(加载有符号数6位)
4) S(s标记),功能不变,影响CPSR标志位
如:mov(不会影响CPSR标志位) movs(CPSR标志位会被置位) -
条件执行后缀,如成立则执行,如不成立则不执行
条件码助记符 | 标志 | 含义 |
---|---|---|
EQ | Z=1 | 相等 |
NE | Z=0 | 不相等 |
CS/HS | C=1 | 无符号数大于或等于 |
CC/LO | C=0 | 无符号数小于 |
MI | N=1 | 负数 |
PL | N=0 | 正数或零 |
VS | V=1 | 溢出 |
VC | V=0 | 没有溢出 |
HI | C=1,Z=0 | 无符号数大于 |
LS | C=0,Z=1 | 无符号数小于或等于 |
GE | N=V | 有符号数大于或等于 |
LT | N!=V | 有符号数小于 |
GT | Z=0,N=V | 有符号数大于 |
LE | Z=1,N!=V | 有符号数小于或等于 |
AL | 任意 | 无条件执行(指令默认条件) |
NV | 任意 | 从不执行(不用使用) |
- 多级流水线:为增加处理器指令流的速度,ARM使用多级流水线,下图为3级流水线工作示意图(S5PV210使用13级流水线,ARM11为8级),每个时钟周期,取指,解码,执行同时执行,PC指向正被取指的指令,而非正在执行的指令
说明:1) 对于ARM来说每个指令4位
2)对于thumb来说每个指令2位
协处理器CP15:https://www.cnblogs.com/lifexy/p/7203786.html
ldm(load register multi) 批量读取内存
stm(store registere mlti) 批量写入内存
eg : stmia sp, {r0 - r12}
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。
一个访存周期同时完成13个寄存器的读写
8种后缀:
- ia(increase after) 先传输,再地址+4
- ib(increase before) 先地址+4,再传输
- da(decrease after) 先传输,在地址-4
- db(decrease before) 先地址-4,再传输
- fd(full decrease) 满递减堆栈
- ed(empty decrease) 空递减堆栈
- fa(full add) 满递增堆栈
- ea(empty add) 空递增堆栈
四种栈:
- 空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而去除时需要先移动一格才能取出
- 满栈:栈指针指向栈中最后一格数据,每次存入时需要移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
- 增栈:栈指针移动时向地址增加的方向移动的栈
- 减栈:栈指针移动时向地址减小的方向移动的栈
!的作用
ldmia r0, {r2 - r3} 将r0寄存器中的地址再内存中对应的值取出放入r2,地址+4,将内存中的值取出放入r3
ldmia r0!, {r2 - r3}
感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。
^的作用
ldmfd sp!, {r0 - r6, pc} 将栈中的值取出放入r0-r6,pc,fd是满减栈,存放时先-4,再存入,读取时先读出,再+4
ldmfd sp!, {r0 - r6, pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。
5.ARM汇编伪指令
伪指令的意义:
- 伪指令和指令的根本区别是经过编译后不会生成机器码
- 伪指令的意义在于指导编译过程
- 伪指令是和具体的编译器相关的,这里主要学习gnu下的汇编伪指令
一些符号的含义:
- @ — 用来注释
- : —以冒号结尾的是标号
- . — 点号在gnu汇编中表示当前指令的地址
- # —— 立即数前面要加#或$,表示这个是个立即数
常用的gnu伪指令:
- .global _start — 给_start外部链接属性
- .section .text — 指定当前段为代码段
- .ascii .byte .short .long .word 定义变量
- .qua .float .string 定义数据
- .align 4 — 以16字节对齐 2^4 =16
- .balignl 16 0xabcdefac —— b表示位填充,align表示要对齐,l表示long, 以4字节为单位填充;16表示16字节对齐,0xabcdefac 表示用来填充的原料
0x00000008: .balignl 16, 0xdeadbeef 0x0000000c 0xdeadbeef
0x00000010: 下一条指令
- .equ — 类似于C中的宏定义
偶尔用到的gnu伪指令
- .end — 标识文件结束
- .include — 头文件包含
- .arm/ .code 32 — 声明以下为arm指令
- .thumb / .code16 — 声明以下为thubm指令
最重要的几个伪指令:
- ldr 大范围的地址加载指令
- adr 小范围的地址加载指令
- adrl 中等范围的地址加载指令
- nop 空操作
ARM中有一个ldr指令,还有一个ldr伪指令,一般都使用ldr伪指令而不用ldr指令
- ldr指令: ldr r0,#0xff
- ldr伪指令: ldr r0,=0xff 涉及大合法/非法立即数,涉及到ARM文字池
adr与ldr的区别和联系
- adr 编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理
- adr 总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
- ldr加载的地址和链接时给的的地址有关,由链接脚本决定
- 差别:ldr加载的地址在链接时确定,而adr加载的地址在运行时确定;所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。