sweetyy、

导航

嵌入式结构与寄存器

嵌入式结构与寄存器

一、计算机组成
1.嵌入式系统
以应用为中心,以计算机技术为基础,软硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗等严格要求的用计算机系统。
(通用计算机系统: PC, 服务器)
嵌入式系统组成
硬件:芯片(CPU+总线+外设控制器)、外围电路……
STM32F407ZETx
软件: bootloader 引导程序
作用:引导其他系统软件运行
在上电的时候,大多数硬件是不能正常工作的,
并且CPU等少数硬件,只能在比较低的频率下工作,
bootloader的作用:
(1) 初始化必要的硬件
(2) 把系统软件(如:OS)引导到内存中运行
OS: Operator System 操作系统
OS是用来管理和分配(硬件)资源的系统软件。

					现代操作系统一个特征是:并发。 “多任务”
					所有任务的执行,都需要CPU,但是CPU的数量
					非常有限。
					调度:决定哪一个任务获取CPU的执行权。
					调度策略
						分时策略  -> 分时系统
							分时间片执行							
							windows   塞班  
							ios  linux  unix   android
							ubuntu   RedHat
						实时策略  -> 实时系统 RTOS: Real Time OS 实时系统 
							每次取优先级最高的那个任务执行,
							直到这个任务结束、或者是它主动放弃CPU、或者
							有更高优先级的任务抢占它。							
							 uCos  freeRTOS  vxworks ...				
								
				应用程序(包括文件系统)……


2. 计算机系统
		冯.诺伊曼结构(普林斯顿结构):
		采用二进制、计算机按照程序顺序执行程序(指令) -> 存储 PC			
		程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置		

​ 冯.诺伊曼把计算机系统分为如下几个部分:
​ CPU(中央处理单元) = 算术逻辑单元(运算器) + 控制电路
​ 存储器 Memory
​ ROM: Read Only Memory 只读存储器
​ RAM: Random Access Memory 随机访问存储器 “R/W”
​ 输入输出设备

​ 冯诺依曼结构总线共用,影响效率

=〉哈佛架构(Harvard Architecture)
		是一种将程序存储和数据存储分开的存储器结构。
		数据存储器与程序存储器采用不同的总线
		C51,ARM9之后嵌入式系统芯片
  1. 各组件是如何通信的?
    通信方式: 通信双方(发送方 W,接收方R )
    全双工
    同一时刻一方即可以发送数据给对方,
    又可以从对方接收数据。

     半双工 cpu--M 数据
     	同一时刻一方只能发送数据给对方,或者
     		是从对方接收数据。
     	下一个时刻,可以改变。
     单工 cpu---->M  地址
     	任意时刻 一方只能发送数据给对方,或者
     		是从对方接收数据。
    

    总线bus:计算机系统内部各组件之间是通过“总线”的来通信的。
    总线两个特点:
    (1) 多个部件可以同时从总线上接收相同的信息 -> 广播式
    (2) 任意时刻只能有一个设备向总线发送信息 -> 系统瓶颈
    (总线协议规定发送规则)

    总线: 按功能分
    数据总线(DB): 双向,宽度差别8bits/16bits/32bits
    地址总线(AB): 单向,CPU发地址,宽度与寻址空间有关 24bit==>16MB
    控制总线(CB): 命令和状态

    按位置分
    片内总线
    系统总线
    通信总线(I/O总线)

    存储器的逻辑结构和操作 图双地址译码
    如:存储器(内存)
    64*64字节存储矩阵:字节编码,需要12 bits


​ 5. CPU工作原理
​ CPU = ALU + Control Unit
​ Register(寄存器):CPU内部的存储区域,暂时存放参与运算的数据和运算结果。
​ 寄存器:锁存器/触发器实现,只包含存储电路的时序逻辑电路。

6.指令流水线技术(略)

​ 7. 相关基本概念
​ (1) 机器字长: 32 CPU一次能处理数据的位数,通常与CPU寄存器位数(总线位数)
​ 有关。字长越大,数的表示范围就大,精度也越高,处理速度也就越快。

​ unsigned int、long、float…… 与机器字长 有没有关系?
​ 前者是软件上编译器相关的一个概念
​ 机器字长是一个 CPU内部结构相关概念
(2) bit:一个存储元件,寄存一位二进制代码
​ 两个状态: 高电平 -> 逻辑 1 低电平 -> 逻辑 0
​ "D触发器,锁存器"

(3) byte :存储元件,包含8bits
​ 一般存储器都是以字节为单位寻址的,每个存储器的每个byte一个地址,字地址。

​ //(4) 基本的数电知识

(5) 芯片= CPU + 总线 + 各硬件控制器

STM32F407ZETx (芯片型号):采用内核是 ARM Cortex M4
说明:
ARM :公司名,只设计CPU,不生产芯片 IP
ARM也是一个CPU型号的名字.
经典:
ARM7
ARM9 -> 三星的S3C2410, S3C2440 ARM920T
ARM10
ARM11 -〉三星的S3C6410,S3C6440


​ Cortex
​ Cortex A
​ Application(应用级产品),对性能要求比较高的产品
​ A8 A9 A10 A15 A53 A72 ...
​ Cortex R
​ Realtime(实时性),对实时要求比较高的产品,
​ 工业级,军工类..
​ R3 R4 R6 R7
​ Cortex M 轻量化
​ MCU(微处理器),“单片机”
​ M0 M1
​ M3 -> STM32F103 意(SGS)法(Thomson)半导体
​ M4 -> STM32F407 429
​ NXP MK60 恩智浦半导体
​ ...


​ 思考:
​ 不同的芯片,采用的同一款处理器内核,有什么不同呢?

芯片厂商:
	Intel 
	TI
	boardcom
	高通
	ST
		意法半导体
			STM8
			STM32				...
			
	NXP 恩智浦
		Freescale 飞思卡尔
		
	三星		
	
	联发科: MTK
	Mstar
	
	华为海思
	全志
	rockchip 瑞芯微
	...	

二、ARM Cortex M4体系结构
​ 1.Cortex M4总线接口:哈佛架构,三套总线
​ (1) Icode总线,用于访问代码空间的指令instruction, 32bits
​ 访问的空间为: 0x0000 0000 ~ 0x1FFF FFFF (512M).
​ 每次取4字节
​ 指令,只读
​ (2) Dcode总线,用于访问代码空间的数据data, 32bits
​ 访问的空间为: 0x0000 0000 ~ 0x1FFF FFFF(512M)
​ 非对齐的访问会被总线分割成几个对齐的访问。
​ “4字节对齐”:地址必须为4倍数。

​ 从最底层来讲: 4字节对齐,CPU写出的地址必须为4的倍数
​ 总线的最后两bit固定为0 可以“另作他用”. (注意R14)
​ 0: 00 00 4: 01 00 8: 10 00
​ 12: 11 00 16: 100 00 ……

如果底层硬件(体系结构)规定是4字节对齐,相应的编译器也会
考虑到对齐的问题.否则效率会变低!

	(3) System 总线,用于访问其他系统空间。如: 各硬件控制器,GPIO……。
		访问空间为 0x2000 0000 ~ 0xDFFF FFFF  和 0XE010 0000 ~ 0xFFFF FFFF
		非对齐的访问会被总线分割成几个对齐的访问			
		I/O总线,用于访问芯片上的各外设寄存器的地址空间的,各种查手册吧……。
		
2. Cortex M4工作状态(处理器状态)
		ARM公司设计的CPU,可以支持两种指令集:
		ARM指令集:32bits,功能比较强大,通用  学
		Thumb指令集:
			thumb 16bits, 功能也强大(Cortex M4只支持Thumb指令) 用
			thumb-2 32bits,功能强大,增加了不少专用的DSP指令
		
		如何实现混合写?取32bits,还是16bits译码呢?				
		处理器状态:CPU正在执行何种指令集。
		ARM状态:CPU正在执行ARM指令集
		Thumb状态:CPU正在执行thumb指令 <---- Cortem M4只支持Thumb指令
		在一个状态寄存器中,专门有一个bit为用来表示处理器的状态
			T:	1  -> Thumb
				0  -> ARM	



​ 作业任务:
​ 1.安装好keil5

​ 创建项目(设备选择:STM32F407ZETx),环境配置选取消 不要选ok
​ output---HEX file勾选
​ 项目设置---debug 中选中 use simulator

  1. Cortex M4寄存器
    ​ 通用寄存器: 没有特殊用途
    ​ R0~R7: 所有thumb thumb-2都可以访问 想用就用
    ​ R8~R12: 只有少量的thumb指令可以访问,thumb-2都可以访问
    ​ why?受bits位数的限制。
    ​ thumb 16bits thumb-2 32bits
    ​ 例子:(自己实现)
    ​ MOV R0,#666
    ​ MOV R1,#0x666
    ​ MOV R2,#1<<3
    ​ MOV R3,#-1
    ​ ADD R8,R0,R1
    ​ SUB R8,R0,R1
    ​ SUB R8,R1,R0

    ​ 专用寄存器: 有专门的用途
    ​ R13 R14 R15 xPSR
    ​ R13(SP): Stack Pointer 保存堆栈的栈顶地址。
    ​ “堆栈(Stack)是什么?” 是用“栈的思想”来管理的一段内存。
    ​ “栈的思想”:先进后出

    ​ 四种:增/减 满/空

    ​ 例如满递减:入栈先-4、出栈后+4

​ 机器求补码,写出补码,包括符号位按位取反,再加1。保护现场:保护寄存器的值(可能改写的值)

​ 为什么需要“堆栈”?为了支持过程调用(函数)。
​ “现场保护”
​ 函数的具体功能的代码
​ “现场恢复”

​ 小端 低对低,高对高

​ 思考:
​ (1) C
​ 有时候,在C语言完成一个功能,我们可以定义一个宏,
​ 也可以定义成一个函数,都可以。请问宏与函数有什么区别?
​ 函数有额外的开销:
​ 现场保护
​ ...
​ 现场恢复
​ 宏是没有

宏定义,容易有二义性,函数又开销大。建议用内联函数,内联函数,没有系统调用的开销,它是直接把代码复制过去。

			(2) 有一些功能或指令比较少的函数了,声明inline.
				inline: 建议编译器,如果可以,把它修饰的函数展开。
					"宏"  


​ 所有支持过程调用的语言,都需要用到栈。

​ Cortex M4有两个堆栈,双堆栈
​ MSP 主堆栈指针

​ PSP 进程堆栈指针

​ 为什么需要双堆栈呢?
​ 为了支持操作系统。把操作系统用的堆栈和用户进程用的堆栈分开。

​ R14(LR): Linked Register 链接寄存器
​ 在执行过程调用的指令的时候,我们需要保存该指令的下一条指针的地址,
​ 因为这个地址,就是需要返回的地址。r14:记住回来的路

​ 有一个专门的寄存器,用来保存过程调用调用的返回地址。->LR(R14)
​ 例子:

​ MOV R0, #3
​ MOV R1, #4
​ BL sum ;
​ // BL:把下一条指令的地址(如下的: (A))存放在LR中
​ //BL:要调用,L表示要回来。R14:过程调用的时候用
​ // 跳转是通过把:要跳到的那个地址,直接赋值给PC
​ // sum -> PC

​ (A) ADD R0, R1,

​ sum:
​ ADD R0,R0,R1
​ MOV PC, LR ; -> return 函数返回,过程返回。

​ R15(PC): Program Counter 程序计数器。 保存下一条要执行的指令的地址。
​ PC会在取指后,会自动增加指令所占的bits位数。
​ 在ARM Cortex M4, PC + 4
​ 在有“指令流水线”情况下,PC的值会有所不同

​ 在程序实现的跳转,就是往PC中赋一个跳转的地址
​ “跳转”? 就是不按顺序执行,就是跳转。
​ 例子:
​ LDR R4,=0x08000008
​ MOV PC,R4
​ xPSR: Program Status Register程序状态寄存器。
​ 程序状态寄存器:保存程序运行过程中的一些状态,这些要保存的状态
​ 分为三类:
​ 应用状态寄存器 APSR: 计算结果的标志
​ N Z C V Q
​ 中断状态寄存器 IPSR: 中断的编号 Exception Number 8bits

​ 执行状态寄存器 EPSR: 执行状态,如: Thumb/ARM

​ => 组合一个32bits的xPSR,如图:

​ N Z C V Q
​ 我们每一条指令的执行都可以影响这些状态标志位。
​ N: 负数标志。
​ xPSR.N < Ret[31] 最高位是符号位 1 -> 负数, 0 -> 非负数

​ 如果 xSPR.N == 1,表示上一条指令的操作结果为负数。
​ N什么什么情况下会置位呢?
​ 如果一条指令,影响状态标志位
​ 指令可以不影响状态标志位,也可以设置为影响标志位

​ 如:
​ MOV R0, #3 ; ->不影响状态标志位,
​ 不管这条指令的执行结果如何,xPSR不变。
​ MOVS R0, #3; ->影响状态标志位。加个s能影响标志位

​ ADDS 两字节指令

				xPSR.N <- 结果寄存器[31]
						如:
							MOVS R0, #3
								R0[31] -> xSPR.N == 0

			Z : Zero 。零标志。结果所有bit位都为0,则xPSR.Z == 1
			[是0不是0]
				否则xPSR.Z == 0.前提也是指令结果影响状态标志。
					如:
						MOVS R0, #0;
								-> xPSR.Z == 1
						MOVS R1, #4 ;
								-> xPSR.Z == 0									
						MOV R2, #0 ;没加S,表示指令的执行,不影响状态寄存器
								-> xPSR.Z值不变。
-----------------------------------------------------------------------------------------								
			C: Carry 借位或进位标志。
				进位: 在做加法运算时,产生了进位。则C == 1,否则 C == 0
				借位: 在做减法运算时,没产生借位。则C == 1,否则 C == 0					
				ADC, ADD, CMN 加法。如果产生了进位,则C == 1,否则 C == 0
				SBC, SUB, CMP 减法。如果生生了借位,则C == 0,否则 C == 1
				1-f会产生借位,1-(-1)有借位,c=0		
				c看最高位的进位、借位,以及加法还是减法
				如:自己补充N、Z、C 标志位变化
				MOV R0,#1    //没有加s,不带符号位,标志位不会变化
				MOV R1,#-1  
				ADDS R2,R0,R1   //加了s,标志位会相应改变
				SUBS R3,R0,R1
				SUBS R4,R1,R0				

-1的原码: 1 0000001
-1的补码: 1 1111111 【注意:符号位不变,从右边找到第一个1,把左边的按位取反】

​ 思考:以下指令执行后对应的N、Z、C 标志位数值(作业)
​ MOVS R0,#-2
​ MOVS R1,#2
​ ADDS R2,R0,R1
​ SUBS R3,R0,R1
​ SUBS R4,R1,R0
​ CMP R0,R1 //比较,实际上是做减法,所以同第④条指令
​ CMP R1,R0 //同⑤

​ V: oVerflow 溢出标志。
​ 反映有符号数做加减运算所得结果是否溢出,如果运算结果超过当前
​ 运算位数所能表示的范围,则溢出 xPSR.V = 1, 否则为0.
​ 在有符号的运算中,进位(借位,C)与溢出是两个完全不同的概念。
​ 例子:
​ CASE 1: 有进位,结果溢出
​ 1111 1111 1111 1111 1111 1111 1111 1111 0xffff ffff (-1)
​ + 1000 0000 0000 0000 0000 0000 0000 0000 0x8000 0000 (-2^31)
​ 1 0111 1111 1111 1111 1111 1111 1111 1111 (+2^31-1)
​ ​ 溢出:
​ ​ 负数 + 负数 = 正数(结果溢出)

​ 代码测试
​ MOV R0,#-1
​ MOV R1,#1<<31 //相当于0x80000000
​ ADDS R2,R0,R1
​ 结果N=0,Z=0,C=1,V=1
相加8000000,溢出

​ CASE 2: 有进位,结果未溢出
​ 0111 1111 1111 1111 1111 1111 1111 1111 (2^31-1)
​ + 1111 1111 1111 1111 1111 1111 1111 1111 (-1)
​ ---------------------------------------------——————
​ 1 0111 1111 1111 1111 1111 1111 1111 1110 (2^31-2)
​ 正数+ 负数 = 正数(结果未溢出)
​ C31=1 C30=1

​ 代码测试:
​ MOVS R0,#0x7fffffff
​ MOVS R1,#-1
​ ADDS R2,R0,R1
​ 结果N=0,Z=0,C=1,V=0

​ CASE 3: 无进位,结果溢出
​ 0111 1111 1111 1111 1111 1111 1111 1111 (2^31-1)
​ + 0000 0000 0000 0000 0000 0000 0000 0001 (1)
​ -----------------------------------------------
​ 1000 0000 0000 0000 0000 0000 0000 0000 (-2^31)
​ 正数+ 正数 = 负数(结果溢出)

​ 代码测试:
​ MOVS R0,#0x7fffffff
​ MOVS R1,#1
​ ADDS R2,R0,R1
​ 结果N=1,Z=0,C=0,V=1
​ C31=0 C30=1

​ CASE 4: 无进位,结果未溢出
​ 1111 1111 1111 1111 1111 1111 1111 1110 (-2)
​ +0000 0000 0000 0000 0000 0000 0000 0001 (1)
​ -----------------------------------------------
​ ​ 1111 1111 1111 1111 1111 1111 1111 1111 (-1)
​ ​ 负数+ 正数 = 负数(结果未溢出)

​ 代码测试:
​ MOVS R0,#0xfffffffe
​ MOVS R1,#1
​ ADDS R2,R0,R1
​ 结果N=1,Z=0,C=0,V=0

​ 结论:最高位V = Cn 异或次高位 Cn-1的结果,判断有无溢出
​ Cn表示最高位的进位(或借位)标志
​ Cn-1表示次高位的进位(或借位)标志

​ Q: 饱和标志
​ 饱和计算: 通过将数据强制置为最大(或最小)允许值,减小了
​ 数据畸变,当然畸变仍然存在,不过若数据没有超过最大
​ 允许范围太多,就不会有太大的问题。

​ 8bits无符号的加法:
​ 普通计算:
​ 11111111 + 1 = 0

​ 11111111
​ + 00000001
​ --------------
​ 100000000

​ 饱和计算
​ 11111111 + 1 = 11111111

​ 例子: 0111111111...11 => 0X7FFF FFFF
​ 1000000000...00 => - (2^31)
​ ADD 普通加法
​ QADD 将两上有符号的32bits相加后,进行饱和操作

​ MOV R0, #0x7FFFFFFF
​ MOV R1, #1
​ ADD R2, R0, R1
​ QADD R3, R0,R1


​ T : xPSR[24]
​ 表示处理器当前执行的状态。(工作状态/处理器状态)
​ ARM状态 xPSR.T == 0
​ Thumb状态 xPST.T == 1

​ IT
​ IF-THEN ,记录if then指令的执行状态

​ 例如
​ instruction status bit for conditional execution
​ IF-THEN 位,它们是if-then指令的执行状态位 (最多4条)
​ 例如:ITTT 三条then
​ 最多四条,如果多于四条,也只有四条

​ if (R0 > R1) // CMP R0,R1
​ {
​ r2 = 2 MOVGT R2, #2 //GT:大于
​ r3 = 3 MOVGT R3, #3
​ r4 = 4 MOVGT R4, #4
​ }
​ ITE 一条then一条else
​ CMP R0,R1
​ MOVGT R2,#2
​ MOVLE R3,#3
例二:



​ ICI : Interruptible-Continuable Instrument 可中断-可继续指令位
​ 与IT共用位置,ICI/IT共用体,你用我就不能用,我用你就不能用
​ 如果执行LDM/STM操作(对内存进行批量拷贝)时,数据没有操作完时,产生一个中断,
​ 中断的优先级很高,必须要打断这条指令的执行,这种情况,
​ 就需要记录被打断的寄存器的编号,在中断响应之后,
​ 处理器返回由该位指向的寄存器,并恢复操作,继续执行拷贝。

​ 中断屏蔽寄存器 P41
​ 中断
​ 异常

​ PRIMASK[0] 片上外设的总中断开关
​ 1 屏蔽所有的片上外设中断
​ 0 响应所有外设的中断

		FAULTMASK[0] 系统错误异常的中断总开关
				1	屏蔽所有异常
				0	响应所有异常
	
		BASEPRI	为使中断屏蔽更加灵活,该寄存器根据中断优先级来屏蔽中断或异常。
				当被设置为一个非0的数值时,它就会屏蔽所有相同或更低优先级的
				异常(包括中断),则更高优先级的中断或异常还会被响应。					
				每一个异常或中断,都会有一个优先级。

​ CONTROL寄存器
​ 用来控制选择哪个堆栈(主堆栈/进程堆栈)
​ 选择线程模式的访问等级(特权等级/非特权等级)
​ 特权等级可以访问所有;
​ 非特权等级只能有限访问
线程的特权模式
​ CONTROL[0] :线程模式的访问等级
​ 1 非特权等级
​ 0 特权等级
​ CONTROL[1] :堆栈的选择
​ 1 进程堆栈 PSP
​ 0 主堆栈 MSP
​ 0x00

  1. Cortex M4工作模式
    "模式": 不同环境,不同的角色

     ARM cortex M4有两种工作模式:
     	Thread Mode: 线程模式
     	Handler Mode: 处理模式(异常中断模式)						
     	
     异常/中断 是什么? 打断CPU指令执行顺序的事件,称为中断。
     
     为什么要支持两种模式呢? 为什么不只用一种模式呢? Thread Mode		
     如果只用一种模式,thread mode,为了响应一些外部事件(比如说,用户是否
     按下某个按键?):
     	轮询:轮流询问。 通过轮询,CPU也可能 响应外部事件,但是
     	轮询天生就有缺陷:
     		(1) 浪费CPU
     		(2) 占用总线, Bus is always busy.
     		(3) 轮询有一个时间差,轮询的时间间隔。不及时!!!
     		
     在CPU内部设计一个 “中断模式”:
     	为了提高效率和响应速度。			
     两种模式之间是怎么切换的呢? 重要。如图thread_Mode与Handler_Mode之间的切换
     
     Handler Mode :
     	中断模式,当一些比较重要的事件,产生时,CPU中止正在做的
     	事情,切换到Handler Mode下去执行,此时 “特权等级”			
     	中断处理完成后,再返回到断点处,继续Thread Mode运行。			
     Thread Mode:
     	线程模式。
     		特权等级 : 可以跑一些如OS的代码
     		非特权等级: 可以跑一些如 "用户态"的代码				
     		特权等级 -> 非特权等级
     	但是:
     		非特权等级 不可以 切换到 特权事件,除非产生“中断”
    

    ​ 注意:不能在调试状态写代码,要关掉debug。进入debug前,两步骤如下:

5、对应关系

posted on 2022-03-06 23:41  sweetyy、  阅读(178)  评论(0)    收藏  举报