ARM体系结构将存储器看作从地址0开始的字节的线性组合。在0~3B放置第1个存储的字数据,在4~7B放置第2个存储的字数据,依次排列。作为32位的微处理器,ARM体系结构支持的最大寻址空间为4GB(2 32 B)。
内存中有两种存储字数据格式,即大端格式和小端格式,具体说明如下。
【大端格式】 在这种格式中,字数据的高字节存储在低位地址中,而字数据的低字节存储在高位地址中。
【小端格式】 与大端格式相反,在小端格式中,低位地址中存储的是字数据的低字节,高位地址中存储的是字数据的高字节。图2-9所示为存储0x12345678字数据的大端格式和小端格式。
图2-9 存储0x12345678字数据的大端格式和小端格式
CM3处理器支持的数据类型有32位字、16位半字和8位字节。CM3之前的ARM处理器只允许对齐数据的传送(以字为单位的传送),其地址的最低两位(4字节)必须是0;对于以半字为单位的传送,其地址最低位必须是0;以字节为单位的传送中无所谓对齐。CM3处理器支持非对齐的传送,数据存储器的访问无须对齐。
存储器的层次结构如图2-10所示。
图2-10 存储器的层次结构
ROM(Read Only Memory,只读存储器)和RAM(Random Access Memory,随机存储器)指的都是半导体存储器。ROM在系统停止供电时仍然可以保持数据不丢失,而RAM通常在掉电后就丢失数据。典型的RAM就是计算机的内存。ROM和RAM的比较如表2-8所示。
表2-8 ROM和RAM的比较
RAM有两大类,即静态RAM(Static RAM,SRAM)和动态RAM(Dynamic RAM,DRAM)。SRAM的读/写速度非常快,是目前读/写速度最快的存储设备,但是它非常昂贵,因此只在要求很苛刻的地方使用,如CPU的一级缓存和二级缓存。DRAM保留数据的时间很短,读/写速度也比SRAM慢,不过它还是比ROM的读速度快,从价格上来说,DRAM比SRAM要便宜得多,通常计算机内存就是DRAM。
ROM也有很多种,如可编程ROM(PROM)、可擦除可编程ROM(EPROM)和电可擦除可编程ROM(EEPROM)等。早期的PROM是一次性的;EPROM可通过紫外线的照射擦除已保存的程序;EEPROM具有电擦除功能,价格较高,写入时间较长,写入速度较慢。
手机软件和通话记录一般存储在EEPROM中(因此可以刷机),但最后一次通话记录在通话时并不存储在EEPROM中,而是暂时存储在SRAM中,因为当时有重要工作(如通话)要做,所以如果写入EEPROM,则漫长的等待是让用户无法忍受的。
Flash存储器又称闪存,它结合了ROM和RAM的长处,不仅具备电可擦除可编程的性能,还不会因断电而丢失数据,同时可以快速读取数据,U盘和MP 3里用的就是这种存储器。过去,嵌入式系统一直将ROM(EPROM)作为其存储设备;近年来,Flash存储器全面替代了ROM(EPROM)在嵌入式系统中的地位。
目前,Flash主要有两种,分别为NOR Flash和NAND Flash。NOR Flash的读取和常见的SDRAM的读取相同,用户可以直接运行装载在NOR Flash中的代码,这样可以减小SRAM的容量,从而节约成本。NAND Flash没有采取内存随机读取技术,它的读取是以一次读取一块的形式来进行的,通常一次读取512B,采用这种技术的Flash存储器比较廉价。用户不能直接运行装载在NAND Flash中的代码,因此许多使用NAND Flash的开发板除了使用NAND Flash,还加上了一块小容量的NOR Flash来运行启动代码。一般小容量的存储器用NOR Flash,因为其读取速度快,多用于存储操作系统等重要信息;而大容量的存储器则用NAND Flash。最常见的NAND Flash应用是嵌入式系统采用的DoC(Disk on Chip)和常用的U盘。
存储器举例如表2-9所示。
表2-9 存储器举例
存储器本身没有地址信息,它的地址是由芯片制造商或用户分配的。给物理存储器分配逻辑地址的过程称为存储器映射,通过这些逻辑地址就可以访问相应存储器的物理存储单元。
在计算机中,最小的信息单位是二进制位(bit),8位组成1字节(Byte,B)。一个存储单元可以存储1B的信息。存储器的容量是以B为最小单位来计算的,对于一个有128个存储单元的存储器,可以说它的容量为128B。一个存储容量为1KB的存储器有1024个存储单元。
存储单元的逻辑地址称为存储地址,一般用十六进制数表示;在每个存储地址对应的存储单元中存储着一组用二进制或十六进制表示的数,通常称之为该存储地址中的内容。
【注意】 存储地址与该存储地址中的内容并不等同。存储地址是存储单元的编号,而存储地址中的内容表示在这个存储单元中存储的数据。
存储一个机器字的存储单元称为字存储单元,与之相对应的存储单元地址称为字地址;而存储一个字节的存储单元称为字节存储单元,与之相对应的存储单元地址称为字节地址。
如果计算机中可以编址的最小单元是字存储单元,则该计算机称为按字寻址的计算机;如果计算机中可编址的最小单位是字节,则该计算机称为按字节寻址的计算机。
如果机器字长等于存储器单元的位数,一个机器字可以包含数个字节,那么一个存储单元也可以包含数个能够单独编址的字节地址。例如,一个16bit字存储单元可存储2B,既可以按字寻址,又可以按字节寻址;当按字节寻址时,16bit字存储单元将占用2B地址。
以STM32为例,STM32是一个32位的微控制器(MP U),它是按字寻址的,它的每个寄存器都占用4B,即32bit。
CM3的存储系统采用统一的编址方式,如图2-11所示。CM3预先定义了“粗线条的”存储器映射,通过把片上外设寄存器映射到外设区,就可以简单地以访问内存的方式来访问这些外设寄存器,从而控制外设的工作。这种预定义的映射关系也可以对访问速度进行优化,而且使得片上系统的设计更易集成。
CM3处理器为4GB的可寻址存储空间提供简单和固定的存储器映射。学习此部分要与2.5.1节介绍的内容对应起来。
CM3的代码区的容量为0.5GB,在存储区的起始端。
CM3片上SRAM的容量是0.5GB,这个区域通过系统总线来访问。如图2-11所示,在这个区的下部,有一个1MB的区间,称为位绑定区;与该位绑定区对应的有一个32MB的位绑定别名区,容纳了8×2 20 个位变量。位绑定区对应的是最低的1MB地址范围,而位绑定别名区中的每个字与位绑定区的1位对应。通过位绑定功能,可以将一个布尔型数据打包在一个单一的字中,从位绑定别名区中像访问普通内存一样使用它们。位绑定别名区中的访问操作是原子的(不可分割),省去了传统的“读—改—写”3个步骤。
与SRAM相邻的0.5GB范围由片上外设的寄存器来使用。在这个区域中也有一个32MB的位绑定别名区,便于快捷地访问外设寄存器,其用法与片上SRAM相同。
还有两个1GB的范围,分别用于连接片外RAM和片外外设。
最后还剩下0.5GB的区域,包括了系统及组件、内部专用外设总线、外部专用外设总线,以及由芯片制造商提供定义的系统外设(Vendor-Specific),数据字节以小端格式存储在存储器中。
图2-11 CM3存储器组织
STM32总线由以下两部分构成。
驱动单元:CM3内核D-Code总线(D-Bus)、I-Code总线(I-Bus)和系统总线(S-Bus)、DMA1总线和DMA2总线。
被动单元:内部SRAM、内部闪存(Flash)、FSMC、AHB到APB的桥(AHB2APBx,连接所有的AP B设备)。
这些都是通过一个多级的AHB相互连接的,如图2-12所示。
I-Code总线将CM3内核的指令总线与闪存指令接口相连接。指令预取在此总线上完成。
D-Code总线将CM3内核的D-Code总线与闪存的数据接口相连接(常量加载和调试访问),用于查表等操作。
系统总线将CM3内核的外设总线连接到总线矩阵。系统总线用于访问内存和外设,覆盖的区域包括SRAM、片上外设、片外RAM、片外扩展设备及系统级存储区的部分空间。
图2-12 STM32的总线结构
DMA总线将DMA的AHB主控接口与总线矩阵相连,总线矩阵协调CPU的D-Code总线和DMA到SRAM、闪存和外设的访问。
总线矩阵还协调内核系统总线和DMA主控总线之间的访问仲裁(利用轮换算法)。总线矩阵由4个驱动部件(D-Code总线、系统总线、DMA1总线和DMA2总线)和4个被动部件(FLITF、SRAM、FSMC和AHB2APBx桥)构成。AHB外设通过总线矩阵与系统总线相连,允许DMA访问。
AHB/APB桥在AHB和两个APB之间提供同步连接。APB1操作速度限于36MHz,APB2操作于全速(最高72MHz)。当对APB寄存器进行8位或16位访问时,该访问会被自动转换成32位访问;桥会自动将8位或16位的数据进行扩展,以配合32位的向量。
STM32将可访问的存储器空间分成8块,每块的容量为0.5GB,其他未分配给片上存储器和外设的空间都是保留的地址空间,如图2-13所示。
代码区(0x00000000~0x1FFFFFFF):可以存储程序代码。它又划分为如下7个功能块。
图2-13 STM32存储器映射
✧ 0x00000000~0x0007FFFF:取决于BOOT引脚,为闪存、系统存储器、SRAM的位绑定别名区。
✧ 0x00080000~0x07FFFFFF:保留。
✧ 0x08000000~0x0807FFFF:片内闪存,编写的程序就存储在该区域(512KB)。
✧ 0x08080000~0x1FFFEFFF:保留。
✧ 0x1FFFF000~0x1FFFF7FF:系统存储器,用于存储ST出厂时写入的ISP自举程序,用户无法改动。使用串口下载时,需要用到这部分程序。
✧ 0x1FFFF800~0x1FFFF80F:选项字节,用于配置读/写保护、BOR级别、软/硬件看门狗,以及器件处于待机或停止模式下的复位。当芯片被锁住后,可以从RAM中启动来修改这部分相应的寄存器位。
✧ 0x1FFFF810~0x1FFFFFFF:保留。
SRAM(0x20000000~0x3FFFFFFF):用于片内SRAM。此区也可以存储程序,用于固件升级等维护工作。
片上外设区(0x40000000~0x5FFFFFFF):用于片上外设。STM32分配给片上外设的地址空间分成3类,APB1总线外设存储地址表如表2-10所示,APB2总线外设存储地址表如表2-11所示,AHB总线外设存储地址表如表2-12所示。如果某款STM32不带有某个片上外设,则该地址范围保留。
外部RAM区的前半段(0x60000000~0x7FFFFFFF)和后半段(0x80000000~0x9FFFFFFF):该区地址指向片上RAM或片外RAM。
外部外设区的前半段(0xA0000000~0xBFFFFFFF)和后半段(0xC0000000~0xDFFFFFFF):用于片外外设的寄存器,也用于多核系统中的共享内存。
系统区(0xE0000000~0xFFFFFFFF):此区是专用外设和供应商指定功能区。
表2-10 APB1总线外设存储地址表
表2-11 APB2总线外设存储地址表
表2-12 AHB总线外设存储地址表
CM3内核是通过I-Code总线、D-Code总线、系统总线与STM32内部的闪存、SRAM相连接的,它直接影响STM32存储器的结构组织。也就是说,CM3的存储器结构决定了STM32的存储器结构。
CM3是一个内核,其自身定义了一个存储器结构,ST公司按照CM3的存储器定义设计出自己的存储器结构。
比较图2-11和图2-13可以发现,STM32的存储器结构与CM3很相似,不同的是,STM32中加入了很多实际的东西(如闪存、SRAM等),正因如此,STM32才成为一个实用的处理器。
将图2-11和图2-13与我们的PC存储器相比,单片机存储器是“平坦”(Flat)的、有限的;而PC存储器有虚拟内存(Virtual Memory),因此是可变的。从图2-11和图2-13中还可以看出,单片机存储器的代码存储在存储器中,变量可以从存储器访问,外设和存储器编址访问形式一样。
内存是一个线性的字节数组(平坦寻址)。每个字节均由8个二进制位组成,都有一个唯一的编号(编号从0开始)。C指针是C语言访问内存单元的有效工具,下面举例说明。
【例2-1】 C指针应用举例。
编写如下C语言程序:
这段程序的运行结果为:
与这段程序的运行结果相关的内存空间示意图如图2-14所示。
图2-14 例2-1内存空间示意图
本例中,指针p1和p2的地址相同,但由于其存储空间类型分别为short和int,因此存储的内容不同。
与位操作类似,位绑定操作将一个地址单元的32位变量中的每一位通过一个简单的地址变换算法映射到另一个地址空间,每一位占用1个地址。对该地址空间的操作,只有数据的最低位是有效的。这样,在对某位进行操作时,就可以不用屏蔽操作,优化了RAM和I/O寄存器的读/写操作,提高了位操作的速度。
CM3中支持位绑定操作的地址区称为位绑定区。在寻址空间还有一个位绑定别名区,从这个地址开始处,每个字(32位)对应位绑定区中的一位,而在位绑定区中,每一位都映射到位绑定别名区中的一个字,对位绑定别名区的访问最终会变换成对位绑定区的访问。
位绑定操作可以使代码量更小,速度更快,效率更高,更安全。一般的操作方式是“读—改—写”的方式,而对位绑定别名区进行的是“写”操作,这种方式可以防止中断对“读—改—写”产生影响。
位绑定还能用于化简跳转程序。以前依据某个位跳转时,必须先读取整个寄存器,然后屏蔽不需要的位,最后比较并跳转。有了位绑定操作后,可以先从位绑定别名区读取状态位,然后比较并跳转。除此之外,其他总线活动不能中断位绑定操作。
支持位绑定操作的两个内存区的范围如下。
0x20000000~0x200FFFFF(SRAM中的最低1MB),如图2-15所示。
图2-15 SRAM中的位绑定区
0x40000000~0x400FFFFF(片上外设区中的最低1MB),如图2-16所示。
说明:在以下论述中,有0x前缀的为十六进制数,没有前缀的为十进制数。
对于SRAM中的位绑定区的某位,记它所在字节地址为 A ,位序号为 n (0≤ n ≤7),则该位在位绑定别名区的地址为
图2-16 片上外设区中的位绑定区
对于片上外设区中的位绑定区的某位,记它所在字节的地址为 A ,位序号为 n (0≤ n ≤7),则该位在位绑定别名区的地址为
在上述两式中,“×4”表示1W=4B,“×8”表示1B=8bit。
下面的映射公式统一给出了位绑定别名区中的每个字(W)与对应位绑定区的相应位(bit)的对应关系:
bit_word_addr=bit_band_base+(byte_offset×32)+(bit_number×4)
式中,bit_word_addr是位绑定别名区中字的地址,它映射到某个目标位;bit_band_base是位绑定别名区的起始地址(对于SRAM中的位绑定区,该地址为0x22000000;对于片上外设区中的位绑定区,该地址为0x42000000);byte_offset是包含目标位的字节在位段里的序号;
bit_number是目标位所在位置(0~7)。
【例2-2】 写出图2-17中的位绑定区与位绑定别名区的对应关系。
(1)位绑定别名区地址0x23FFFFE0与位绑定区地址0x200FFFFF的字节中的第0位对应:
0x23FFFFE0=0x22000000+(0xFFFFF×32)+0×4
(2)位绑定别名区地址0x23FFFFFC与位绑定区地址0x200FFFFF的字节中的第7位对应:
0x23FFFFEC=0x22000000+(0xFFFFF×32)+7×4
(3)位绑定别名区地址0x22000000与位绑定区地址0x20000000的字节中的第0位对应:
0x22000000=0x22000000+(0×32)+0×4
(4)位绑定别名区地址0x2200001C与位绑定区地址0x20000000的字节中的第7位对应:
0x2200001C=0x22000000+(0×32)+7×4
【例2-3】 SRAM中地址为0x20000300的字节中的第2位,对应位绑定别名区中地址是多少?
0x22006008=0x22000000+(0x300×32)+(2×4)
图2-17 位绑定区与位绑定别名区的对应关系图
对0x22006008地址的写操作与对SRAM中地址0x20000300的字节中的第2位执行“读—改—写”操作有着相同的效果。读0x22006008地址返回SRAM中地址0x20000300的字节中的第2位的值(0x01或0x00)。
【例2-4】 在SRAM的0x20004000地址中定义一个长度为512B的数组。
该数组首字节的bit0对应的位绑定地址为
0x22000000+(0x4000×32)+(0×4)=0x22080000
【例2-5】 将例2-4中定义的数组的每一位通过GPIO A输出。
GPIO A的端口输出数据寄存器位于地址0x4001080C,对GPIO A的PIN 0来说,控制其输出的位绑定地址为
0x42000000+(0x1080C×32)+(0×4)=0x42210180
将数组中的数据通过GPIOA的PIN 0口输出,若不使用位绑定功能,则其代码为:
若使用位绑定功能,则其代码为:
由此可见,使用位绑定功能后,运算量和代码量均大为减小。
为了方便操作,可以将式(2-1)与式(2-2)合并成一个公式,将“位绑定区地址+位序号”转换成位绑定别名区地址的一个宏:
addr&0xF0000000是为了区别SRAM和片上外设区,实际效果就是取出4或2:若为片上外设区,则取出的是4,加上0x02000000后就等于0x42000000,这是片上外设区中的位绑定别名区的起始地址;若为SRAM,则取出的是2,加上0x02000000后就等于0x22000000,这是SRAM中的位绑定别名区的起始地址。屏蔽addr&0x00FFFFFF的高3位,相当于减去0x20000000或0x40000000(因为片上外设区的最高地址是0x20100000,与起始地址0x20000000相减,总是低5位有效,所以把高3位屏蔽掉,以达到减去起始地址的效果;具体屏蔽多少位与最高地址相关)。“<<5”相当于前面公式中的“×8×4”,而“<<2”相当于“×4”。
有了宏定义,就可以通过指针的形式操作这些位绑定别名区地址,最终实现位绑定区的位操作。例如:
〖说明〗
1MB的位绑定区对应32MB的位绑定别名区,容纳了8×2 20 个位变量。
必须以字对齐的方式访问位绑定别名区,否则会产生不可预料的结果。
对位绑定区可进行字操作,也可进行位操作。
由于并无实际的物理存储器与位绑定别名区对应,因此对位绑定别名区不能进行字操作,只能配合位绑定区进行位操作。
在多任务中,位绑定操作可用于实现共享资源在任务间的互锁访问。多任务的共享资源必须满足一次只有一个任务访问它,即所谓的原子操作。之前的“读—改—写”需要3条指令来实现,这3条指令之间存在2个能被中断的“空当”。CM3的位绑定操作将“读—改—写”变成一个硬件级别支持的原子操作,使之不能被中断。
虽然位绑定区的1bit经过“膨胀”后,变为4B,但仍是最低位才有效,即实际定义bool类型变量都是用int来实现的。也就是说,定义一个bool变量b1,编译器实际分配了32bit内存空间来存储这个bool变量b1。按理说,仅需要1bit就够用了,编译器为什么要浪费31bit的内存空间呢?这涉及权衡节省内存空间与提高运行效率的问题:以前内存很贵,计算机上的内存空间较小,编写程序时主要考虑的是如何节省内存空间;如今内存变得很便宜了,因此编写程序时主要考虑如何提高运行效率。虽然位绑定操作浪费了31bit的内存空间,但其运行效率得到了提高。因为STM32F103的系统总线是32bit的,按照4B进行访问是最快的,所以1bit“膨胀”成4B,访问是最高效的。因此,可以通过指针的形式访问位绑定别名区地址,从而达到操作位绑定区1bit的效果。