STM32
直播中

YYXIAO

8年用户 1166经验值
擅长:接口/总线/驱动
私信 关注
[问答]

可编程器件的编程原理是什么?

可编程器件的编程原理是什么?

指令集对CPU的意义是什么?

回帖(1)

常静娜

2021-11-30 11:48:38
一、可编程器件的编程原理

1.1、电子器件的发展方向

模拟器件——>数字器件
ASIC(专用芯片,一个芯片只能完成一个任务)——>可编程器件
1.2 、可编程器件的特点

CPU在固定频率的时钟控制下有节奏运行;
CPU可以通过总线读取外部存储设备中的二进制指令集,然后解码执行;
这些可以被CPU解码执行的二进制指令集是CPU设计的时候确定的,是CPU的设计者(ARM公司)定义的,本质上是一串由1和0组成的数字。这就是CPU的汇编指令集。
1.3 、整个编程及运行过程

程序员用汇编指令编程——>经汇编器会变成二进制可执行程序文件——>二进制文件被CPU读取进去——>CPU内部电路对二进制文件解码(即对命令进行解析)——>解码通过则CPU执行指令、完成指令动作。
如果程序员用C语言等高级语言编程,则编译器先将C语言程序编译为汇编程序,再进行上面的后续部分。
在Linux中,Elf格式的文件是可执行文件;而在windows中后缀为.exe的文件是可执行文件。## 1.4 、从源代码到CPU执行过程




在Linux中,Elf格式的文件是可执行文件;而在windows中后缀为.exe的文件是可执行文件。
二、指令集对CPU的意义

2.1、 汇编语言与C等高级语言的差异

汇编难写,C好写;
汇编无可移植性,C语言有一定可移植性,Java等更高级语言移植性更强;
汇编语言效率最高,C语言次之,Java等更高级语言效率更低;
汇编不适合完成大型复杂的项目,更高级语言更适合完成更大、更复杂的项目;
注意:语言没有好坏之分,只有合适与不合适之分。要注意区分哪种情况用什么语言,就需要了解语言的特性。
2.2 、汇编语言的本质

汇编的本质是机器指令(机器码)的助记符,是一种低级符号语言。
机器指令集是一款CPU的编程特征,是这款CPU的设计者指定的。CPU的内部电路设计就是为了实现这些指令集的功能。机器指令集就好像CPU的API接口一样。
指令的实现体现在硬件上就是电路,通过设计一系列的电路从而实现这个指令的功能;
2.3 、编程语言的发展过程

纯机器码编程;
汇编语言编程;
C语言编程;
C++语言编程;
Java C#等语言编程;
脚本语言编程;
2.4 、总结

汇编语言就是CPU的机器指令集的助记符,是一款CPU的本质特征
不同CPU的机器指令集设计不同,因此汇编程序不能在不同CPU间互相移植;
使用汇编编程可以充分发挥CPU的设计特点,所以汇编编程效率最高,因此在操作系统内核中效率极其重要处都需要用汇编处理;
三、RISC和CISC的区别

3.1、CISC

complex instruction -set computer 复杂指令集CPU
CISC体系的设计理念是用最少的指令来完成任务(譬如计算乘法只需要一条MUL指令即可),因此CISC的CPU本身设计复杂、工艺复杂,但好处是编译器好设计。CISC出现较早,至今Intel还一直采用CISC设计。
3.2、RISC

Reduced Instruction-Set Computer 精简指令集CPU
RISC的设计理念是让软件来完成具体的任务,CPU本身仅提供基本功能指令集。因此RISC CPU的指令集只有很少的指令,这种设计相对于CISC ,CPU的设计和工艺简单了,但是编译器的设计变难了。
3.3、CPU设计方式发展

早期简单CPU,指令和功能都很有限;
CISC年代——CPU功能扩展依赖于指令集的扩展,实质是CPU内部组合逻辑电路的扩展;
RISC年代——CPU仅提供基础功能指令(譬如内存于寄存器通信指令,基本运算与判断指令等),功能扩展由使用CPU的人利用基础架构来灵活实现。
3.4、 RISC 与 CISC 指令数对比

一般典型CISC CPU指令在300条左右;
ARM CPU常用指令30条左右;
3.5 、发展趋势

没有纯粹的RISC 或 CISC,发展方向是RISC与CISC结合,形成一种介于2者之间的CPU类型。
四、统一编址&独立编址&哈佛结构&冯诺依曼结构

4.1 、什么是IO?什么是内存?

CPU的运行速度很快,而硬盘的读取速度很慢,如果直接从硬盘读取数据交给CPU处理,就会大大降低效率,所以想了一个办法就是在CPU中增加了一二三级缓存,这个缓存的读取速度就大大提高了,但是成本很高,所以空间很小。于是,就出现了内存,内存的读取速度介于缓存和硬盘之间,价格也合适,空间也介于缓存和硬盘之间。所以内存就成了用来存放运行程序的场所。
内存是程序的运行场所,内存和CPU之间通过总线连接,CPU通过一定的地址来访问具体内存单元;
IO(input and output)是输入输出接口,是CPU和其它外部设备(如串口、LCD、触摸屏、LED等)之间通信的道路。一般的,IO就是指CPU的各种内部或外部外设(的寄存器)。
4.2 、内存的访问方式

内存通过CPU的地址总线来寻址定位,然后通过CPU数据总线来读写。
CPU的地址总线的位数是CPU设计时确定的,因此一款CPU所能寻址的范围是一定的,而内存是需要占用CPU的寻址空间的。
内存与CPU的这种总线式连接方式是一种直接连接,优点是效率高访问快,缺点是资源有限,扩展性差。
4.3、IO的访问方式

IO指的是与CPU连接的各种外设。
CPU访问各种外设有2种方式:一种是类似于访问内存的方式,即把外设的寄存器当作一个内存地址来读写,从而以访问内存相同的方式来操作外设,叫IO与内存统一编址方式(RISC思想);另一种是使用专用的CPU指令来访问某种特定外设,叫IO与内存独立编址(这是CISC的思想)。
4.4 、对比

由于内存访问频率高,因此采用总线式连接,直接地址访问,效率最高。
IO与内存统一编址方式,优势是IO当作内存来访问,编程简单;缺点是IO也需要占用一定的CPU地址空间,而CPU的地址空间是有限资源。
IO与内存独立编址方式,优势是不占用CPU地址空间,缺点是CPU设计变复杂了。
4.5 、程序和数据

程序运行时两大核心元素:程序 + 数据
程序是我们写好的源代码经过编译、汇编之后得到的机器码,这些机器码可以拿给CPU去解码执行,CPU不会也不应该去修改程序,所以程序是只读的
数据是程序运行过程中定义和产生的变量的值,数据是可以读写的,程序运行实际就是为了改变数据的值。
4.6 、冯诺依曼结构和哈佛结构

程序与数据都放在内存中,且不彼此分离的结构称为冯诺依曼结构。譬如Intel的CPU均采用冯诺依曼结构。
程序和数据分开独立放在不同的内存块中,彼此完全分离的结构称为哈佛结构。譬如大部分的单片机(MCS51、ARM9等)和嵌入式均采用哈佛结构。
4.7 、优劣对比

冯诺依曼结构中程序和数据不区分的放在一起,因此安全和稳定性是个问题,好处是处理起来简单。
哈佛结构中程序(一般放在ROM-Read Only Memory、flash中)和数据(一般放在RAM中)独立分开存放,因此好处是安全和稳定性高,缺点是软件处理复杂一些(需要统一规划链接地址等)。
五、软件编程控制硬件的关键——寄存器

5.1、 什么是寄存器

寄存器属于CPU外设的硬件组成部分;
CPU可以像访问内存一样访问寄存器;(IO与内存统一编址)
寄存器是CPU的硬件设计者制定的,目的是留作外设被编程控制的“活动开关”;
正如汇编指令集是CPU的编程接口API一样,寄存器是外设硬件的软件编程接口API 。使用软件编程控制某一硬件,其实就是编程读写该硬件的寄存器。
5.2、寄存器的演示

请看S5PV210 datasheet;
编程操作寄存器类似于访问内存;
寄存器中每个bit位都有特定含义,因此编程操作时需要位操作;
单个寄存器的位宽一般和CPU的位宽一样,以实现最佳访问效率;
5.3 、两类寄存器

SoC中有2类寄存器:通用寄存器和SFR
通用寄存器(ARM中有37个)是CPU的组成部分,CPU的很多活动都需要通用寄存器的支持和参与;
SFR(special function register,特殊功能寄存器)不在CPU中,而存在于CPU的外设中,我们通过访问外设的SFR来编程操控这个外设,这就似乎硬件编程控制的方法。
六、ARM体系结构要点总结

6.1、ARM是RISC架构

常用ARM汇编指令只有二三十条;
ARM是低功耗CPU;
ARM的架构非常适合单片机、嵌入式,尤其是物联网领域;而服务器等高性能领域目前主导还是Intel;
6.2、ARM是统一编址的

大部分ARM(M3 M4 M7 M0 ARM9 ARM11 A8 A9等)都是32位架构;
32位ARM CPU支持的内存少于4G,通过CPU地址总线来访问;
SoC中的各种内部设备通过各自的SFR编程访问,这些SFR的访问方式类似于访问普通内存,这叫IO与内存统一编址。
6.3、ARM是哈佛结构的

常见ARM(除ARM7外)都是哈佛结构的;
哈佛结构保证了ARM CPU运行的稳定性和安全性,因此ARM适用于嵌入式领域;
哈佛结构也决定了ARM裸机程序(使用实地址即物理地址)的链接比较麻烦,必须使用复杂的链接脚本告知链接器如何组织程序;对于OS之上的应用(工作在虚拟地址之中)则不需考虑这么多。
七、S5PV210的内存映射详解

7.1、什么是内存映射?

S5PV210属于ARM Cortex-A8架构,32位CPU,CPU设计时就有32根地址线&32根数据线。
32根地址线决定了CPU的地址空间为4G,那么这4G空间如何分配使用?这个问题就是内存映射问题(好像准确点应该叫地址映射吧,大家注意这个小坑)。
7.2、SPV210 datasheet中内存映射位置






7.3、一些专业术语

ROM: read only memory只读存储器 (注意:这里的只读不是说不能写,而是说不能直接通过地址总线和数据总线去写,只读是可以通过地址总线和数据总线直接去读)
RAM: ramdom access memory随机访问存储器
IROM: internal rom内部ROM,指的是集成到SoC内部的ROM
IRAM: internal ram内部RAM,指的是集成到SoC内部的RAM
DRAM: dynamic ram动态RAM
SRAM: static ram静态RAM
SROM: static rom? sram and rom?
ONENAND/NAND:
SFR: special function register
注意:记住DRAM0和DRAM1的起始地址,IROM和IRAM的起始地址。
八、CPU和外部存储器的接口

内存:内部存储器,用来运行程序的,RAM,举例:DRAM,SRAM,DDR
外存:外部存储器,用来存储东西的,ROM,举例:硬盘,Flash(Nand,iNand)
CPU连接内存和外存的连接方式不同。内存需要直接地址访问,所以是通过地址总线&数据总线的总线式访问方式连接的(好处是直接访问,随机访问,坏处是占用CPU的地址空间,大小受限);外存是通过CPU的外存接口来连接的(好处是不占用CPU的地址空间,坏处是访问速度没有总线式快,访问时序较复杂)
8.1、SoC常用外部存储器

NorFlash 可以总线式访问,接到SROM_bank,优点是可以直接总线访问,一般用来启动。
——————————————————————————————————————
(这些都是Nand,CPU不能总线式访问)
NandFlash:分为SLC和MLC
eMMC/iNand/moviNand eMMC(embeded MMC),iNand是sanDisk公司出产的eMMC,moviNand是三星公司出产的eMMC
oneNAND 是三星公司出的一种Nand
SD卡/TF卡/MMC卡
eSSD
(以上全是Flash)
——————————————————————————————————————
SATA硬盘(机械式访问、磁存储原理、SATA是接口)
8.2 、S5PV210支持的外部存储器

见datasheet Section2.6 booting sequence
见 datasheet Section5全部,memory
见 datasheet Section8.7 SD/MMC部分
8.3、X210开发板支持的外部存储器

X210有2个版本,Nand版和iNand版,分别使用Nandflash和iNand为外部存储器。我们使用的是iNand版本,板载4GB iNand
S5PV210共支持4个SD/MMC通道,其中通道0和2依次用作启动。X210开发板中SD/MMC 0通道用于连接板载MMC,因此外部启动时只能使用SD/MMC 2通道(注意通道3不能启动)。见《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》中P6
8.4 、总结

1、现代SoC支持多种外部存储器;
2、外部存储器主要用来存储程序(可执行代码),相当于电脑的硬盘。
3、各种不同外设存储器原理不同,大小、性价比不同,一般产品厂家根据需要选择适合自己产品的外存使用。
4、外部存储器和CPU连接一般不是通过地址&数据总线直接相连,因为地址空间不够用。一般都是通过专门的接口来连接的。
九、S5PV210的启动过程

9.1、内存

SRAM:静态内存,特点就是容量小、价格高,优点是不需要软件初始化直接上电就能用;CPU内部的cache都是SRAM。
DRAM:动态内存,特点就是容量大、价格低,缺点就是上电后不能直接使用,需要软件初始化后才可以使用。
单片机中:内存需求量小,而且希望开发尽量简单,适合全部用SRAM;
嵌入式系统:内存需求量大,而且没有NorFlash等可启动介质;
pc机:内存需求量大,而且软件复杂,不在乎DRAM的初始化开销,适合全部用DRAM;
9.2、外存

NorFlash:特点是容量小、价格高,优点是可以和CPU直接总线式相连,CPU上电后可以直接读取,所以一般用作启动介质;
NandFlash(跟硬盘一样):特点是容量大、价格低,缺点是不能总线式访问,也就是说不能上电CPU直接读取,需要CPU先运行一些初始化软件,然后通过时序接口读写。
所以一般PC机都是:很小容量的BIOS(NorFlash) + 很大容量的硬盘(类似于NandFlash) + 大容量的DRAM
一般的单片机:很小容量的NorFlash + 很小容量的SRAM
嵌入式系统:因为NorFlash很贵,所以现在很多嵌入式系统倾向于不用NorFlash,直接用:外接的大容量Nand + 外接大容量DRAM + SoC内置SRAM
9.3、S5PV210使用的启动方式

是:外接的大容量Nand + 外接大容量DRAM + SoC内置SRAM;
实际上210的启动还要更好玩一些,210内置了一块96KB大小的SRAM(叫iRAM),同时还有一块内置的64KB大小的NorFlash(叫iROM),iROM中预先内置烧录了一些代码(称为iROM代码),iRAM属于SRAM(不需软件初始化,上电即可使用)。(这里有一个问题为什么需要设计iROM和iRAM?:是为了支持多种外部设备启动。)
210的启动过程大致是:
第一步:CPU上电后先从内部iROM中读取预先设置的代码(BL0),执行。这一段iROM代码首先做了一些基本的初始化(CPU时钟、关看门狗…)(这一段iROM代码是三星出厂前设置的,三星也不知道我们板子上将来接的是什么样的DRAM,因此这一段iROM是不能负责初始化外接的DRAM的,因此这一段代码只能初始化SoC内部的东西);然后这一段代码会判断我们选择的启动模式(我们通过硬件跳线可以更改板子的启动模式),然后从相应的外部存储器(在BL0代码执行中,会初始化块设备赋值函数,这个可以在文档中查询,即直接调用内嵌的API函数即可)去读取启动代码(BL1,大小为16KB)到内部SRAM。
第二步:从SRAM去运行刚上一步读取来的BL1(16KB),然后执行。BL1负责初始化NandFlash(外部存储设备),然后将BL2读取到SRAM(剩余的80KB),然后运行。第三步:从SRAM运行BL2,BL2初始化DRAM,然后将OS读取到DRAM中,然后启动OS,启动过程结束。
思路:因为启动代码的大小是不定的,有些公司可能96KB就够了,有些公司可能1MB都不够,所以刚才说的2步的启动方式不合适。三星的解决方案是:把启动代码分为2半(BL1和BL2),这两部分协同工作来完成启动。
注意:外部的硬盘等设备就叫做块设备
启动过程主要参考《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》
9.4 、second boot support

When 1 st boot mode fails, SD/MMC boot will be tried through SD/MMC channel 2 with 4-bit data
当第一启动模式失败时,SD/MMC卡启动模式下将会从SD/MMC2通道尝试再次启动。
这种二级启动是一种冗余设计。SoC中第一启动介质故障而导致不能启动时,可以从备用启动介质启动。我们做裸机实验时从SD2启动就是利用了这一设计特性。
9.5、使用iROM启动的好处

1、降低BOM成本。因为iROM可以使SOC从各种外设启动,因此可以省下一块boot rom(专门用来启动的rom,一般是norflash)
2、支持各种校验类型的nand
3、可以在不使用编程器的情况下使用一种外部存储器运行程序来给另一种外部存储器编程烧录。这样生产时就不用额外购买专用编程器了,降低了量产成本。
9.6、BL0做了什么?

关看门狗
初始化指令cache
初始化栈
初始化堆
初始化块设备复制函数device copy function
设置SoC时钟系统
复制BL1到内部IRAM(16KB)
检查BL1的校验和
跳转到BL1去执行
9.7、S5PV210的所有启动

先1st启动,通过OMpin选择启动介质
再2nd启动,从SD2
再Uart启动
再USB启动
wakeup status 唤醒状态(复位):启动有时候是真正的关机重启,有时候是休眠后启动。
十、如何在开发板上选择不同的启动方式

10.1、SoC通过OMpin来识别外部启动介质

具体的如何拨码可以查看《S5PV210_iROM_Application》的Boot Configration(第22页)部分以及《210开发板硬件手册》的拨码开关设置部分(第12页)。
10.2、从SD0的eMMC启动

开发板默认是从eMMC启动,内部烧录了Android。
OMpin设置为:101100
10.3、从SD2启动

可以使用外置SD卡从SD2通道启动,但这需要先破坏板载的eMMC中的android镜像。破坏方法见九鼎官方的裸机教程文档《x210v3开发板裸机教程.pdf》中2.5.2节(P19)
这时OMpin设置和SD0启动一样。
总结:这里需要注意,从SD2启动时,OMpin的设置和SD0启动是一样的。SD0启动和SD2启动不是一个并联的关系,而是一个串联的关系。只要第一步启动失败,第二步启动都是从SD2启动。第一步启动不仅仅只有eMMC,还有其它方式。如下图所示:




绿色框表示第一步启动的所有方式,这个跟OMpin的设置有关系,如何设置可以查手册,红色框表示第二步启动,这个和前面的OMpin没有关系,不管设置的第一步启动是什么,只要第一步启动失败,都会执行第二步启动SD2。
10.4、USB调试模式

通过查手册,知道其OMpin设置为:101101
10.5、总结

(1)拨码开关设置我们只需动OM[5]即可,其他几个根本不需要碰。需要SD启动时OM5打到GND,需要USB启动时OM5打到VCC
(2)可以先不销毁eMMC中的android,而使用USB启动来做裸机调试。之后课程中我们会使用USB启动和SD卡启动两种方式共同来完成实验,让大家对比学习。
十一、ARM的编程模式和七种工作模式

11.1、ARM的基本设定

ARM采用的是32位架构。
ARM约定:



  • Byte: 8 bits
  • Halfword: 16 bits(2byte)
  • word: 32 bits(4byte)
  • 大部分ARM core提供:
  • ARM指令集(32-bit)
  • Thumb指令集(16-bit)
  • Thumb2指令集(16 & 32 bit)

11.2、ARM处理器工作模式

ARM有7个基本的工作模式:



  • User:非特权模式,大部分任务执行在这种模式;
    ——————————————————————————————————————
  • FIQ:当一个高优先级(fast)中断产生时将会进入这种模式
  • IRQ:当一个低优先级(normal)中断产生时将会进入这种模式;
  • Supervisor:当复位或软中断指令执行时将会进入这种模式;
  • Abort:当存取异常时将会进入这种模式;
  • Undef:当执行未定义指令时会进入这种模式;
    ——————————————————————————————————————
  • System:使用和User模式相同寄存器集的特权模式;

11.3、注意

(1)除User(用户模式)是Normal(普通模式)外,其他6中都是Privilege(特权模式)。
(2)Privilege中除Sys模式外,其余5种为异常模式;
(3)各种模式的切换,可以是程序员通过代码主动切换(通过写CPSR寄存器),也可以是CPU在某些情况下自动切换。
(4)各种模式下权限和可以访问的寄存器不同。
11.4、CPU为什么要设计这些模式?

(1)CPU是硬件,OS是软件,软件的设计要依赖硬件的特性,硬件的设计要考虑软件需要,便于实现软件特性。(这就跟人是一样的,你与父母,与亲人都要互相考虑,你多为她考虑,她才会为你考虑。你为人人,才会有人人为你,所以很多时候不要只考虑到自己,要多为对方考虑,换位思想)
(2)操作系统有安全级别要求,因此CPU设计多种模式是为了方便操作系统的多种角色安全等级需要。
十二、ARM的37个寄存器(通用寄存器)详解

12.1、ARM寄存器











总结:
(1)ARM共有37个寄存器,都是32位长度;
(2)37个寄存器中30个为“通用”型,1个固定用作PC,一个固定用作CPSR,5个固定用作5种异常模式下的SPSR;
(3)ARM总共有37个寄存器,但是在每种模式下最多只能看到18个寄存器,其他寄存器虽然名字相同但是在当前模式下是不可见的。
(4)对r13这个名字来说,在ARM中共有6个名叫r13(又叫sp)的寄存器,但是在每种特定处理器模式下,只有一个r13是当前可见的,其他的r13必须切换到其他的对应模式下才能看到。这种设计叫影子寄存器(banked register)
(5)r0~r7,CPSR寄存器,PC寄存器在ARM中只有一个,即所有状态共享这些寄存器。
12.2、CPSR程序状态寄存器






Mode位:处理器模式位
————————————————
中断禁止位:
I= 1: 禁止 IRQ.
F = 1: 禁止 FIQ
T Bit :
仅ARM xT架构支持
T = 0: 处理器处于 ARM 状态(ARM指令集)
T = 1: 处理器处于 Thumb 状态(Thumb指令集)
————————————————
Q 位:(不用掌握)
仅ARM 5TE/J架构
支持指示饱和状态
J 位:(不用掌握)
仅ARM 5TE/J架构支持
J = 1: 处理器处于Jazelle状态
————————————————
条件位:
N = Negative result from ALU
Z = Zero result from ALU
C = ALU operation Carried out(进位)
V = ALU operation oVerflowed(溢出)
————————————————
注意:
(1)CPSR各个bit位表明了CPU的某些状态信息,这些信息非常重要,和后面学到的汇编指令息息相关(譬如BLE指令中的E就和CPSR中的Z标志位有关)
(2)CPSR中的I、F位和开中断、关中断有关。
(3)CPSR中的mode位(bit0 ~ bit4共5位)决定了CPU的工作模式,在uboot代码中会使用汇编进行设置。
12.3、PC(r15)程序控制寄存器

(1)PC(Program control register)为程序指针,PC指向哪里,CPU就会执行哪条指令(所以程序跳转时就是把目标地址代码放到PC中)。
(2)整个CPU中只有一个PC(CPSR也只有一个,但SPSR有5个,因为有5种异常模式)。
十三、ARM的异常处理方式简单介绍

13.1、什么是异常

正常工作之外的流程都叫异常;
异常会打断正在执行的工作,并且一般我们希望异常处理完成后继续回来执行原来的工作,所以在异常处理之前,都会有一个现场保护的工作。
中断是异常的一种;
13.2、异常向量表

所有的CPU都有异常向量表,这是CPU设计时就设定好的,是硬件决定的。
当异常发生时,CPU会自动动作(PC跳转到异常向量处处理异常,有时伴有一些辅助动作)。
异常向量表是硬件向软件提供的处理异常的支持;
13.3 、ARM的异常处理机制






当异常产生时,ARM core:
-拷贝CPSR(程序状态寄存器)到SPSR_
-设置适当的CPSR位:
(1)改变处理器状态进入ARM态;
(2)改变处理器模式进入相应的异常模式;
(3)设置中断禁止位禁止相应中断(如果需要);
-保存返回地址到LR_
-设置PC为相应的异常向量
——————————————————————
返回时,异常处理需要:
-从SPSR_恢复CPSR
-从LR_恢复PC
-Note:这些操作只能在ARM态执行;
13.4 、总结

(1)异常处理中有一些是硬件自动做的,有一些是程序员需要自己做的。需要搞清楚哪些是自己做的,才知道如何写代码。
(2)以上说的是CPU设计时提供的异常向量表,一般称为一级向量表。有些CPU为了支持多个中断,还会提供二级中断向量表,处理思路类似于这里说的一级中断向量表。
十四、ARM汇编指令集

14.1 、两个概念:指令和伪指令

(1)(汇编)指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。
(2)(汇编)伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。
14.2 、两种不同风格的ARM指令

(1)ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如:LDR R0,[R1]
(2)GNU风格的ARM汇编:指令一般用小写字母、Linux中常用。如:ldr r0,[r1](我们一般用这个)
14.3、ARM汇编特点1:LDR/STR架构

(1)ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。
(2)ldr(load register)指令将内存内容加载入通用寄存器;
(3)str(store register)指令将寄存器内容存入内存空间中;
(4)ldr/str组合用来实现ARM CPU和内存数据交换;
14.4、ARM汇编特点2:8种寻址方式

寻址方式:处理器根据指令中给出的地址信息来寻找物理地址的方式;
寄存器寻址 【mov r1,r2】 将寄存器r2的值存入r1
立即数寻址 【mov r0,#0xFF00】 以#开头的表示数字,即将十六进制数0xFF00存入寄存器r0
寄存器移位寻址 【mov r0,r1,lsl #3】 lsl表示左移,即将寄存器r1中的值左移3位,然后存入r0
寄存器间接寻址 【ldr r1,[r2]】 寄存器r2中存入的值是一个内存地址,然后将该地址中的数加载到寄存器r1中。
基址变址寻址 【ldr r1,[r2,#4] 】 寄存器r2中存入的值是一个内存地址,在这个地址上加4获得目的地址,将该地址中的数加载到寄存器r1中。
多寄存器寻址 【ldmia r1!,{r2-r7,r12}】 r1中存放的是一个内存地址,并以这个地址为起始地址,依次往后读取数据(就可以看成是一个数组),存放入寄存器r2-r7以及r12中。
堆栈寻址 【stmfd sp!,{r2-r7,lr}】 将寄存器r2-r7以及lr中的值依次压入栈sp中。
相对寻址 【beq flag】flag:(这个叫标号)表示后面语句的起始地址,就是一个入口点。
14.5 、ARM汇编特点3:指令后缀

(1)同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
B(byte):功能不变,操作长度变为8位;
H(Half word):功能不变,长度变为16位;
S(signed):功能不变,操作数变为有符号
如:ldr ldrb ldrh ldr*** ldrsh
S(S标志)功能不变,影响CPSR标志位
如:mov和movs
一般 mov r0 #0,这个的话CPSR的标志位是不变的
而如果是 movs r0 #0,这个的话CPSR的标志位要改变
14.6、 ARM汇编特点4:条件执行后缀






【mov r0,r1】 相当于C语言中的r0 = r1;
【moveq r0,r1】 如果eq后缀成立,则直接执行mov r0,r1;如果eq不成立则本句代码直接作废,相当于没有。类似于C语言中if(eq){r0 = r1;}
条件后缀执行注意2点:
(1)条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果;
(2)条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否被执行;
14.7、 ARM汇编特点5:多级指令流水线

为增加处理器指令流的速度,ARM使用多级流水线,下图为3级流水线工作原理示意图。(S5PV210使用13级流水线,ARM11为8级)
-允许多个操作同时处理,而非顺序执行。





-PC指向正被取指的指令,而非正在执行的指令;而正在被执行的指令的地址是当前PC指向的地址减8.这个就是红色框图中表示的。
14.8、 常用ARM指令1:数据处理指令

数据传输指令: mov mvn
算术指令: add sub r*** adc ***c rsc
逻辑指令: and orr eor bic
比较指令: cmp cmn tst teq
乘法指令: mvl mla umull umlal smull smlal
前导零计数: clz
mov(move):
mov r1,r0 两个寄存器之间数据传递
mov r1,#0xff 将立即数赋值给寄存器
mvn和mov的用法一样,区别是mov是原封不动的传递,而mvn是按位取反后传递
按位取反的含义(1变0,0变1):
譬如:r1 = 0xff(或者补充完成为0x000000ff),然后mov r0,r1后,r0 = 0xff; 但是使用mvn r0,r1后,r0 = 0xffffff00;
and 逻辑与
orr 逻辑或
eor 逻辑异或(相同的为0,不同为1)
bic 位清除指令
【bic r0,r1,#0x1f】 将r1中的数的bit0到bit4清零后赋值给r0 (这里清零的位数由0x1f决定,有多少个1就有多少个bit被清零,0x1f = 0x 00000000…00011111)
【bic r0,r0,#0x1f】 将r0寄存器中的数读出来,然后将bit0到bit4清零后重新赋值给r0.注意:寄存器不是直接清零,而是需要先读出来然后清零后放回r0.
比较指令:
cmp: cmp r0,r1 等价于 sub r2,r0,r1(r2 = r0 - r1 )
cmn: cmn r0,r1 等价于 add r0,r1
tst: tst r0,#0xf 测试r0的bit0 - bit3是否全为0
teq 是对两个数进行EOR。
比较指令用来比较2个寄存器中的数。
注意:比较指令不用后加s后缀就可以影响cpsr中的标志位;
14.9 、常用ARM指令2:cpsr访问指令

mrs&msr:
mrs用来读psr,msr用来写psr;
CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr。
cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个Soc中只有1个;而spsr有5个,分别在5种异常模式下,作用是当从普通模式进入异常模式时,用来保存之前普通模式下cpsr的,以在返回普通模式时恢复原来的cpsr。
14.10 、常用ARM指令3:跳转(分支)指令

b & bl &bx
b:直接跳转(就没打算返回),一般用于绝对跳转
bl:(branch and link)跳转前把返回地址放入lr中,以便返回,以便用于函数调用;
bx:跳转同时切换到ARM模式,一般用于异常处理的跳转;(基本用不到)
14.11 、常用ARM指令4:访存指令

ldr/str & ldm/stm & swp
单个字/半字/字节访问 【ldr/str】
多字批量访问 【ldm/stm】
【swp r1,r2,[r0]】 将r0保存的内存地址中的值放入寄存器r1中,然后将r2中的值存入刚才的内存地址中
【swp r1,r1,[r0]】 如果内存中的数是5,寄存器r1中的数是3,这句指令的意思就是将内存中的5赋给r1,将r1被赋值之前的3存入内存,这样就完成了内存与寄存器的直接交换数据。
14.12、 ARM汇编中的立即数

合法立即数与非法立即数;
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数。
如:合法立即数:【0x000000ff】 【0x00ff0000】 【0xf000000f】
非法立即数:0x000001ff,因为非零部分已经超过了8位。
14.13、 常用ARM指令5:软中断指令

swi(software interrupt):软中断指令用来实现操作系统中系统调用
注意:汇编的话,只要能看懂uboot就可以了。
十五、ARM协处理器和协处理器指令详解

15.1、 协处理器cp15操作指令

mcr & mrc
mrc用于读取cp15中的寄存器
mcr用于写入cp15中的寄存器
15.2 、什么是协处理器

(1)SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。(2)ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15(cp:coprocessor)
(3)协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。
15.3、mrc和mcr的使用方法






opcode_1:对于cp15永远为0;
Rd:ARM的普通寄存器;
Crn:cp15的寄存器,合法值是c0-c15;
Crm:cp15的寄存器,一般均设为c0;
opcode_2:一般省略或为0;
15.4 、举例(来自于uboot)

mrc p15,0,r0,c1,c0,0
orr r0,r0,#1
mcr p15,0,r0,c1,c0,0
其他见uboot源码start.S中相关代码;
15.5 、协处理器学习要点

不必深究,将uboot中和kernel中起始代码中一般操作搞明白即可。
只看一般用法,不详细区分参数细节,否则会陷入很多复杂未知中。
关键在于理解,而不在于记住。
关于协处理器中每个寄存器中每一位代表什么意思可以上网去查,不用死记硬背,也没有必要。
十六、ldm/stm与栈的处理

16.1、为什么需要多寄存器访问指令

(1)ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm。
(2)ldm(load register multiple)
(3)stm(store register multiple)
16.2 、举例(uboot start.s 537行)

stmia sp,{r0 - r12}
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址…直到r12内容放入(0x3001048),指令完成。
一个访存周期同时完成13个寄存器的读写;
16.3、 8种后缀

(1)ia(increase after) 先传输,再地址+4
(2)ib(increase before) 先地址+4,再传输
(3)da(decrease after) 先传输,再地址-4
(4)db(decrease before) 先地址-4,再传输
(5)fd(Full decending) 满递减堆栈
(6)ed(empty decending) 空递减堆栈
(7)fa(full ascending) 满递增堆栈
(8)ea(empty ascending)空递增堆栈
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈;
当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈;
递增堆栈:堆栈由低地址向高地址生成;
递减堆栈:堆栈由高地址向低地址生成;
16.4 、!的作用

ldmia r0,{r2-r3}
ldmia r0!,{r2-r3}
感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值;
16.5 、^的作用

ldmfd sp!,{r0 - r6,pc}
ldmfd sp!,{r0 - r6,pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。
16.6、 总结

(1)批量读取或写入内存时要用ldm/stm指令
(2)各种后缀以理解为主,不需记忆,最常见的是stmia和stmfd;
(3)谨记:操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈。
十七、ARM汇编伪指令

17.1伪指令的意义

(1)伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。(2)伪指令的意义在于指导编程过程。
(3)伪指令是和具体的编译器有关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
17.2 gnu汇编中的一些符号

(1)@用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似;(2)#做注释,一般放在行首,表示这一行都是注释而不是代码。(这个去查以下,写代码的时候可以试一下)
(3):以冒号结尾的是标号;
(4).点号在gnu汇编中表示当前指令的地址
(5)#立即数前面要加#或$,表示这是个立即数;
17.3 常用gnu伪指令

(1).global _start @给_start外部链接属性
(2).section _text @指定当前段为代码
(3).ascii .byte .short .long .word
(4).quad(双字) .float .string @定义数据
(5).align 4 @以16(24)字节对齐
(6).balignl 16 0xabcdefgh @16字节对齐填充
(7).equ @类似于C中宏定义
IRQ_STACK_START:
.word 0x0badc0de
等价于 unsigned int IRQ_STACK_START = 0x0badc0de;
【.balignl 16,0xdeadbeef 】 @对齐 + 填充(b表示bit,以位来填充;l表示long,以4字节为单位来填充;align表示对齐;16表示16字节对齐;0xdeadbeef 是用来填充的原料)
17.4 偶尔会用到的gnu伪指令

(1).end @标识文件结束
(2).include @头文件包含
(3).arm/.code32 @声明以下为arm指令
(4).thumb/.code16 @声明以下为thumb指令
17.5 最重要的几个伪指令

. ldr 大范围的地址加载指令
. adr 小范围的地址加载指令
. adrl 中等范围的地址加载指令
. nop 空操作
.ARM中有一个ldr指令,还有一个ldr伪指令(在使用ldr指令时需要考虑后面的立即数是否合法,如果不合法会出错;伪指令则不需要考虑后面的立即数是否合法)
.一般都使用ldr伪指令而不用ldr指令
ldr指令: ldr r0, #0xff
ldr伪指令 : ldr r0, =0xff
17.6 adr与ldr

adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
adr总是以PC为基准来标识地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里;
ldr加载的地址和链接时给定的地址有关,由链接脚本决定。
adr和ldr的区别:ldr加载的地址在链接时确定,而adr加载的地址在运行时确定;所以我们可以通过adr和ldr加载的地址比较来判断当前程序是否在链接时指定的地址运行。
特别说明:关于本专栏中的文章皆来源于朱有鹏老师嵌入式核心课程笔记
举报

更多回帖

发帖
×
20
完善资料,
赚取积分