ARM体系结构学习笔记

嵌入式: 
    应用层:  编程基础,   IO 多进程多线程  网络编程  数据库  C++/qt     应用 工程师 
    底层:    嵌入式的入门课程  
        ARM体系结构 与 接口编程:  硬件(CPU)如何运行程序?  程序如何控制硬件的?    裸机工程师 
        系统移植:    在硬件上 移植操作系统   系统平台搭建 
        Linux驱动开发:  在Linux系统中  编写安装 定制的设备驱动 
        STM32开发 :
        
    项目开发:    

ARM体系结构: 
    硬件(CPU)如何运行程序?
接口编程: 
    程序如何控制硬件的?
    
交叉开发:  程序的编写 编译 在 PC机上(宿主机)  但 运行在 开发板(目标机)

计算机的基本组成部分: 
    输入设备 
    输出设备
    存储器 
    运算器
    控制器
    总线 

计算机  X64(64位)  x86(32位) stm32   8位(C51,STM8)  16位
    
存储器:  用于存储程序运行过程中的一些数据  缓存 
    金字塔结构   速度排序 
    
    寄存器 : 速度最快  成本最高的 存储器  通常以"个"计  在CPU中 相当于CPU的"手"
    高速缓存: cache    速度 等同与CPU    成本高  通常以 KB或M计 
    内存(RAM):  内存条  运行内存     程序需要RAM(代码,数据段)  成本一般   G 
    外存(ROM):  硬盘  U盘  光盘      网盘 
        硬盘:   成本低     T  
            机械硬盘: 转速  5400  7200    硬盘接口:  STAT(600m)  M.2(固态)  SCACS 
            固态硬盘: 使用FLASH 存储   读写有限次数   写操作要 复杂与写操作 
                          通常 写入数据前  需要擦除  且 是按块擦除的 
                          访问 读 通常也按块读取 
            负载均衡:                
CPU中:            
    运算器: ALU 做运算 + - ... 
    控制器: 统筹整个系统的 工作, 仲裁      

CPU: 中央处理器  一般不能独立运行程序 只有寄存器 运算器 控制器 总线 
MCU: 微控制器    可以独立运行程序  即 有CPU + RAM + ROM 构成 
FPU: 硬件浮点运算单元   专门用于 进行浮点运算  
GPU: 多个FPU + 多控制器构成   核心多  128 256 
APU: (AI核心)  硬件神经网络  

总线: 专门用于 CPU与各个设备间通信  
    单总线: 
    多总线: 

嵌入式开的的特点:
    开发环境的不同:  交叉开发环境 
    以应用为中心, 围绕实际功能设计 软件和硬件   量体裁衣
    
ARM公司: 
    提出ARM架构  使用精简处理器指令集 
    整体上, ARM架构芯片 功耗低,成本低 
    嵌入式应用中,大部分CPU使用ARM架构 
    
ARM架构: ARM公司开发的 一种CPU处理器结构 

ARM生态链: 
    ARM公司: 做CPU架构设计 ARM  出解决方案的 
    芯片公司:  三星,华为,...   设计制造芯片  
    开发板设计公司: 做开发板  以及基本驱动程序  
    产品开发公司:   大量中小型嵌入式企业  
        使用开发板或自己设计硬件开发板  做  应用
    
    软件层面:
    Linux系统: 开源免费  GPL协议 
    智能手机: 嵌入式崛起  安卓系统 
    
ARM环境搭建: 
    使用KEIL 调试功能,模拟ARM CPU运行情况 
    
    1. 安装交叉编译工具  即编译器   参考环境搭建试验手册 
        该编译器在 Windows下运行 但编译的程序在ARM架构下运行 
        
ARM CPU 架构发展和性能变化
    ARM7
    ARM9
    ...
    cortex系列:
    Cortex-A :   高性能型  多用于 多媒体 图形图像 UI 
    Cortex-R :   实时性能强  多用于汽车 运动控制等 
    Cortex-M :   低功耗或 低成本  场景  多作为单片机使用 
        stm32 - Cortex-M3 
    
cpu运行程序逻辑: 
    CPU在时钟系统的 驱动下, 不断从ARM中 取出预先编译好的 程序指令  然后 执行
        
机器指令: 有汇编或其他高级语言编译 得到的 二进制指令 CPU只能识别这个     

armCPU寄存器:  是cpu留给程序的接口  类似于人的双手 
    CPU中 主要有 R0-R12  13个通用寄存器    没有专用的特定功能 
                 r13 sp  栈指针寄存器      专用于存储程序栈指针  
                 r14 lr  链接寄存器         用于程序跳转返回的实现 或 模式切换
                 r15 pc  程序计数器        用于存储下一条将要执行的指令的地址 
                                           实际存储的是取址的指令地址 
                                           修改该寄存器中的值 即可实现程序跳转  
                 CPSR: 程序状态寄存器      用于存储程序状态(ZNCV位 ...)
                    nzcv为: 条件位 存储计算过程中的一些特定情况 
                    模式位: [4:0] 存储CPU的模式  修改模式位 可以主动让CPU进入指定模式
                    irq/fiq使能位 ...
                 SPSR: 程序状态备份寄存器 
                 
CPU状态:
    ARM状态:   在该状态下  运行ARM指令   4字节对齐  NOP 运行速度快  指令更耗费空间 
    thumb状态: 在该状态下  运行thumb指令 2字节对齐      指令密度更高  运行效率会有所降低
    
CPU模式:   目的1 为了安全  2 为了处理异常 
    ARMCPU 提供了 7种模式  每一种模式 都有 R0-R15 CPSR  SPSR(usr,sys模式没有)
            usr        sys     abort     svc      irq    fiq         undef 
            用户模式   系统     终止     管理员   中断   快速中断   未定义
模式位      10000     11111    10111     10011    10010  10001      11011 

寄存器个数  17个       0       3           3      3        8         3
ARMcpu 一共有 37 个寄存器    有7中模式 通常 

    非特权模式 : usr 
    特权模式:  除usr模式外      有特权 可以主动修改cpsr
        非异常模式:  sys  
        异常模式 :   除sys和usr外   指在CPU发生异常情况时 会主动进入的模式  

什么叫异常?  CPU中的异常是指,CPU遇到的一些特定情况   将这些特定情况 统称为异常     
    armcpu有哪些异常? 
    复位异常:  复位键被点击了 产生了复位信号                   CPU将 自动进入svc模式 
    未定义指令异常:  CPU从内存中得到了一条不识别的指令                   undef  用于扩展指令 
    软中断异常 :    触发了软中断                                           svc    用于实现系统调用
    指令预取异常:    CPU从内存中取一条指令失败了                         abort  用于虚拟内存的刷新 
    数据异常 :       a/0                                                   abort  处理非法运算
    中断异常 :       触发了中断                                           irq   处理中断事件
      快速中断异常     触发了快速中断                                       fiq   处理快速中断事件
        
中断: 指 事件(中断源) 打断CPU当前正在 执行的程序, 转而去 处理中断事件 
        处理完毕后,回到打断位置 继续向后执行 
        
(中断源): 系统中  能够产生中断信号的来源  
    外部中断:  中断信号来自芯片外部 通过GPIO 传入的 
    内部中断:  芯片内部的一些设备 产生的  timer  uart  wdt ....
    
异常处理流程: 
    硬件处理:  由CPU在触发异常时 自动完成的动作  
    4大步 3小步 
        1. 备份 CPSR 到对应模式的 SPSR     为了恢复程序状态做准备 
        2. 备份 PC到 对应模式的LR          为了处理完毕异常后 可以返回到异常打断处理继续 
        3. 修改 CPSR 
            1) 设置CPU 为ARM状态           因为异常的处理 都是ARM指令 
            2) 设置CPSR 中模式位 为对应异常模式
            3) 禁止某些中断(f/i) 若有必要   例如 fir触发时 irq中断位设置为1 屏蔽 
        4. 设置PC 到对应异常向量的入口   即程序入口         

异常向量的入口???    有CPU 规定的 对应异常的处理程序的入口 地址
异常向量重定向 功能  CPU支持  
入口:    
0x00    复位异常:  复位键被点击了 产生了复位信号                   CPU将 自动进入svc模式 
0x04    未定义指令异常:  CPU从内存中得到了一条不识别的指令                   undef  用于扩展指令 
0x08    软中断异常 :    触发了软中断                                           svc    用于实现系统调用
0x0c    指令预取异常:    CPU从内存中取一条指令失败了                         abort  用于虚拟内存的刷新 
0x10    数据异常 :       a/0                                                   abort  处理非法运算
0x14    保留             为了后续可以 扩展异常 
0x18    中断异常 :       触发了中断                                           irq   处理中断事件
0x1c      快速中断异常     触发了快速中断                                       fiq   处理快速中断事件
        
    软件处理:  由编程者解决具体的问题 
    1, 保存现场   公用寄存器入栈  r0-r12, lr 
    2, 处理异常   
    3, 恢复现场   从栈中取出 R0-R12, lr--> PC , 恢复CPSR 从 spsr中恢复    
    
异常的优先级问题? 
    1. reset 复位异常 
    2、Data Abort
    3、FIQ
    4、IRQ
    5、Prefetch Abort
    6、SWI
    7、Undefined instruction
    
思考?  
    fiq 快速中断    irq 中断  
    问 快速中断为何比中断快 ,快在什么地方 
        1.fiq 优先级高于irq 
        2.fiq 独立寄存器多  保存现场 与 恢复现场 快 
        3.fiq 位于异常向量表 末尾, 可以不必要跳转 直接开始处理 
        
流水线:   目的 提高指令执行的效率 
    一条ARM指令的执行  主要有3个步骤   
    取址: 从内存中 加载 指令到 CPU 
    译码: 解锁执行需要的寄存器 
    执行: 指令执行对应操作 
    
    假设 取址  5周期   译码  3周期    执行  1周期 
    执行一条指令 9周期 
    有100条执行 需要顺序执行   900周期
    若为3级流水线  问 100条指令 需要多少周期可以完成?  515个周期 
    
多级流水线:  3级   5级  9级  11级    
    指令执行的周期 并非一致   多周期指令 将 阻塞流水线 
    流水线打断     执行了 跳转指令  异常触发了  

ARM指令集:  RISC:精简指令集处理器      追求的是 结构的简化 功耗的降低 
                    指令长度是固定  舍弃了一些复杂指令 转而由多个简单指令取完成复杂指令的工作
                    嵌入式应用场合, 低功耗, 对成本敏感的 场景 
    
    X86     CISC:复杂指令集处理器    追求的是 运行效率     
                    指令集长度不固定  所有指令都追求硬件实现   结构复杂 功耗高 
                    
ARM汇编指令:    目的 认识汇编, 从而更好的进行C语言编程 
    RAM指令格式:  了解 
    4字节宽度  地址4字节对齐    方便寻址 
    
    指令码组成部分 :
    condition:  高4bit[31:28]  条件码  0-15 16个值 
        条件码: 用于指令的 条件执行 , ARM指定绝大部分 都可以条件执行 
        可以让程序 不必要跳转 而执行分支结构  不打断流水线 效率更高   
    
    指令操作码:  [24:21]  指令本身的 编号  可以有16种指令 
    Rn: 第一操作寄存器  第一个操作对象寄存器    
    Rd: 目标寄存器      用于存放输出结果的寄存器 
    Rm: 第二操作寄存器 或 立即数 或 立即数移位
    S位:  用于指定 该指令 是否记录其状态到 CPSR中 
    
汇编编程格式: 
    1. 汇编指令    编译后将 生成一条对应值 指令码  
    2. 汇编伪指令  编译后将  生成一条或多条指令码 
    3. 汇编操作(标志符号)   .text  .end  ...   loop:

.text  汇编代码段开始  属于汇编操作
mov r0,#1   @ 汇编指令  
.data  汇编数据段开始 

.end   汇编代码结束

汇编中的类型: 
 ARM 约定:
  Byte:8bits(1byte)    字节     char
  Halfword:16bits(2byte)  半字 short
  Word:32bits(4byte) 字    int
  Doubleword:64bits (cortex-a)

数据搬移: mov  mvn(~)  
立即数:   使用12bit 按规则可以存放的数 称立即数
    0 - 2^12-1  
    12bit分两部分   高4bit 存放 循环右移的偶数次  A 10
                    低8bit 存放 右移的数本身      FF 
                    
立即数定义:  一个数 可以通过一个8bit数  循环右移偶数位得到, 该数即立即数 
            目的  扩大了 可以输入的值的 值域范围 0-2^32 但不连续 
mov r0,#0xff000
    
    0000 0000 0000 1111 1111 0000 0000 0000
    
    1111 1111  循环右移 10*2 20位得到 
    
    0000 0000 0000 1111 1111 0000 0000 0000    
    
循环右移:     桶型移位器  硬件设备  
    
练习:      判断下列数 是否为立即数 
   100            是
0x F000000F     0xff 循环右移 4位  是立即数 
0X 0F0F0F0F        不是立即数 
0X 12300        不是立即数 
0x 12a00        是 
0X 8070         不是 
    
0X 12300        
0000 0000 0000 0001 0010 0011 0000 0000 
    
0X 12A00        
0000 0000 0000 0001 0010 1100 0000 0000     
    
0X 1060      不是 
0000 0000 0000 0000 0001 0000 0110 0000 
    
mvn 将数 进行取反后在进行装载 
    有效数: 一个数取反后是立即数
    
伪指令: 
    ldr r0,=0x12345678
    
移位操作指令: 
lsl    逻辑左移:  对无符号数操作  <<   高位移出丢弃  低位补0 
lsr    逻辑右移:  对无符号数操作  >>   低位移出丢弃  高位补0 
asr    算数右移:  对有符号数操作  >>   低位移出丢弃  高位补符号位 
ror    循环右移:  低位移出  补充到高位 
1111 1111 1111 1111 1111 1111 1111 1011       >> 1   = -5
 
1111 1111 1111 1111 1111 1111 1111 1101           = -3
                                       
1111 1111 1111 1111 1111 1111 1111 1110           = -2 
    
1111 1111 1111 1111 1111 1111 1111 1111           = -1    
C语言实现循环右移:
unsigned int  ROR(unsigned int a, char cnt)
{
    int f = 0;
    while(cnt--)
    {
        f =  a & 1;  //取出低位
        a >> 1;     // 右移 
        a = a | (f << 31);  // 补充高位
    }
}
    
算数指令:  在指令后+s 后缀 表示该指令要影响cpsr中的nzcv位 若存在
    add   加法 
    adc   带进位加法 
    sub   减法 
    sbc   带借位减法 
    rsb   逆向减法
    rsc   带借位逆向减法 
    mul   乘法指令 
    
32位寄存器 实现64位加法:
    a = 0x12345678 87654321 
    b = 0x12345678 88888888 
    a+b 0x2468acf1 0fedcba9
    a-b 0xffffffff fedcba99  

    r0 高32bit  r1 低32bit 
    计算: a-b 使用ARM汇编实现

为运算指令:
位运算     C语言:  &按位与  |按位或   ^按位亦或    ~按位取反  mvn 
                and       orr         eor 
逻辑运算:    !非  &&逻辑与   ||逻辑或  

and  &  运算规则:  按位操作  与0得0  与1不变 
orr  |             按位操作  或1得1  或0不变 
eor  ^             按位操作  相同为0 不同为1 

0000 0000 0000 0000 0000 0000 1111 1111
0000 0000 0000 0000 0000 0000 0000 1111    
0000 0000 0000 0000 0000 0000 1111 0000

位操作的 应用:
    有一个数 a  [31:0]  希望 其中 [7:4] = 0 其他bit不变 
a = **** **** **** **** **** **** [****] ****    
a & 1111 1111 1111 1111 1111 1111  0000  1111    ~ 1111 0000 
a & ~ 1111 0000
a = a & ~ (0xf << 4);
  = **** **** **** **** **** **** [0000] ****    

    有一个数 a  [31:0]  希望 其中 [7:4] = 0x7 其他bit不变 
a = **** **** **** **** **** **** [****] ****
a & 1111 1111 1111 1111 1111 1111  0000  1111
  = **** **** **** **** **** **** [0000] ****
a | 0000 0000 0000 0000 0000 0000  0111  0000 
a | (0x7 << 4)
  = **** **** **** **** **** **** [0111] ****
  
  a = (a & ~ (0xf << 4)) | (0x7 << 4);
  
  有一个数 a  [31:0]  希望 其中 [18:9] =  其他bit不变 
 a = **** **** **** *[*** **** ***]* **** ****
   & ~(0x3ff << 9); 
 a = **** **** **** *[000 0000 000]* **** **** 
   | (0x123 << 9)

    a = (a & ~ (0x3ff << 9)) | (0x123 << 9);

    有一个数 a  [31:0]  希望 其中 [m:n] = x 其他bit不变 
    a = (a & ~ ( P << n)) | (x << n);   其中 P= 2^(m-n+1) - 1;  P本质是 位宽这么多个1的值
    
    判断一个数  a [31:0]  的 [8:4] == 0x12 ? 
    **** **** **** **** **** ***[* ****] ****
    0000 0000 0000 0000 0000 0000  000[* ****]  == 0x12 
    对一个数 的 某些bit进行比较判断 
    if( (  (a >> 4) & 0x1f ) == 0x12 )
    
    
位操作指令:
    bic  位清除指令  用于清除指定的位 
比较指令:  执行结果 在 CPSR 中的 NZCV位体现  不需要目标寄存器 
    TST  测试指定位 是否为0
    cmp  比较大小   >  <  ==  !=  >=  <= 
    teq  比较相等否   ==  != 
    
if(a > 0)
    a= 5;
else        // <= 0
    a= 6;

条件执行与 条件码:   在指令后边 添加条件后缀  表示条件执行  
    共有16种条件     就是根据cpsr中的nzcv位的 状态来决定这条指令 要执行否 
    EQ   相等执行 
    NE   不相等执行 
    有符号数比较
    GT   > 
    GE   >= 
    LT   < 
    LE   <= 

跳转指令: 
    b   : 不带链接的跳转   类似 goto   实现循环  
    bl  : 带链接的跳转  就是 跳转时 会 将PC指针备份到 lr寄存器中   实现函数调用

汇编实现:
求 1+2+3 +.. 100 = ???

for(int i =1, sum = 0; i<= 100; i++)  sum += i;
mov r0,#1
mov r1,#0
loop:
cmp r0,#100
addle r1,r0
addle r0,#1
ble loop      @ 跳转到loop标记处 

作业:
编译如下C代码为汇编指令:     
switch(a)
{
    case 1: a=5; break;
    case 2: a=6; 
    case 3: a=7; break;
    default: a=10;
}
end:
    a=1 ===> a=5
    a=2 ===> a=7
    a=3 ===> a=7
    a=4 ===> a=10
    
mov r0,#4
teq r0,#1  @ case 1:
moveq r0,#5 @相等时执行
beq _end      @break
teqne r0,#2 @ case 2: 前提是case1 没有命中
moveq r0,#6  @ a = 6
teqne r0,#3  @ case 3:
moveq r0,#7  @ a = 7
beq _end      @break
movne R0,#10   @default
_end:
nop


bl指令:  主要作用于函数调用 
int addNum(int a,int b, int c)
{
    return a+b+c;
}    
    
a = addNum(1,2,3);    
    
C语言标准规范: 
    规定 函数的调用  参数 前4个参数使用 r0-r3传递  超过4个的参数 使用栈传递 
    返回值 通过R0传递 

@ 准备参数 
MOV R0,#1
MOV R1,#2
MOV R2,#3
@ 调用函数 
bl addNum
nop
nop
nop

@ 汇编定义函数
addNum:  @ r0,r1,r2 3个参数
    add r0,r1
    add r0,r2
    @ return 
    mov pc,lr    

快捷键:    
shift + alt + @   选择列 
    
b,bl指令的特点: 
    1.b/bl指令 是一个相对跳转指令  相对当前取址的指令位置  向前或先后 跳转  指定的指令条数
    2.b/bl指令  最大可以跳转的 地址空间 为  +- 32M 
        指令除去 8BIT的条件码与指令码  剩余24bit 用于存储跳转的 指令条数 
        Linux程序地址空间 0-4G
    b/bl属于短跳转  +- 32M 
    长跳转/绝对跳转  直接将要跳转的内存地址 加载到PC寄存器中  
    
内存操作指令:
    ldr : 读内存 数据到 寄存器中
    str : 写内存 将寄存器中的 数据 写入到内存 
        一次读写 4个字节 
    ldrb / strb  一次读写 1个字节
    ldrh / strh  一次读写 2个字节 
    
int a =5;
内存操作:     
.data   @ 数据段
@ int a =5
a:  .word 5
@ char c = 'a'
c:  .Byte 'a'
    .Byte 0   @ 空一个内存位置  以满足下一个内存地址是2字节对齐的
@ short x = 7
x:  .short 7

@ int arr[5] = {1,2,3,4,5}
arr: .word 1,2,3,4,5

思考 如何访问数组?
arr[1] ... 

前索引:  
ldr r2,[r0, #4]   @ arr[1]  *(arr+1)

指针方式遍历数组:
int *p =arr;
while(p <= arr+5) *p++;  // 先*p 然后再 p++; 

@ 后索引
@ldr r1,[r0],#4     @ *p++;
@ldr r2,[r0],#4
@ldr r3,[r0],#4
@ ....
@ 自动索引         
ldr r1,[r0,#4]!    @ *++p;  先p++,然后再*P

练习: 求数组元素的和 
int *p =arr;
int sum  =0;
while(p <= arr+5) sum += *p++;

多寄存器操作指令: 
    ldm  :  连续从内存中 读取多个整型数据到 指定的多个寄存器中 
    stm  :  将多个寄存器中的 数据  写入到 连续的内存中  
    
栈: 
    满栈:  栈指针指向的位置 有数据 
    空栈:  栈指针指向的位置 无数据 
    增栈:  存入数据时 栈指针在向大地址方向移动  
    减栈:  存入数据时 栈指针在向小地址方向移动   

    满增栈:  入栈 *++p   出栈 *p--;
    满减栈:  入栈 *--p   出栈 *p++;
    空增栈:  入栈 *p++   出栈 *--p; 
    空减栈:  入栈 *p--   出栈 *++p; 
    
    C标准指定: 栈使用 满减栈  fd后缀 
    
了解: 函数调用中  栈的操作流程     
    
    
    
    
    


    
    
    


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值