2.2 Memory model
1. 内存区域、类型及属性
内存被分成不同的区域,不同区域有着不同的类型及属性;内存的类型及属性决定着访问这些区域时的行为。
内存的类型有:
- Normal,处理器可以为了效率而重新排序事务,或者执行推测性的读取。
- Device, 处理器将保持相对于其他Device或者Strongly-ordered内存的事务顺序。
- Strongly-ordered, 处理器将保持相对于其他内存的事务顺序。
- Execute Never (XN),处理器禁止指令访问。从XN区域获取指令的任何尝试都会导致内存管理错误异常。
内存系统可以缓冲对设备内存的写操作,但不能缓冲对Strongly-ordered内存的写操作。
2. 内存访问的系统顺序
对于大多数由显式内存访问指令引起的内存访问,内存系统不保证访问完成的顺序与指令的程序顺序相匹配,前提是这不会影响指令序列的行为。
通常,如果正确的程序执行取决于按程序流程完成的两次内存访问,软件必须在内存访问指令之间插入一条内存屏障指令。
然而,内存系统确实保证了对设备的访问和强顺序内存的一些顺序。对于两个内存访问指令A1和A2,如果在程序顺序中A1出现在A2之前,则由这两条指令引发的内存访问的顺序:
3. 内存访问的行为
Code, SRAM, 和外部RAM区域都可以存储程序,但是还是推荐使用Code区域运行程序,因为这里可以同时获取指令和访问数据。
MPU,内存包含单元,可以重载默认的内存访问行为。
4. 内存访问的软件顺序
在程序流程的指令顺序不一定总能保证和相应的内存事务顺序一致,这是因为:
- 处理器可以重新排序一些内存的访问来提高效率,前提是不会影响指令序列的行为。
- 处理器有多重总线接口
- 在内存区域中的内存或设备有不同的等待状态
- 一些内存的访问已被缓存或者被预测到了
如果内存的访问顺序很关键,软件必须包含内存屏障指令:
- DMB, Data Memory Barrier, 确保显著的内存事务完成在随后的内存事务之前
- DSB, Data Synchronization Barrier,确保内存事务完成在随后的指令执行之前
- ISB, Instruction Synchronization Barrier, 确保所有内存事务完成后的效果被随后的指令所识别
比如,在以下情况下使用内存屏障指令:
- 向量表:如果程序修改了向量表中的入口,然后使能了相应的异常。它们之间可以使用DMB,以保证如果异常在修改入口后立即发生时处理器可以使用新的入口地址。
- 自我修改代码:如果程序中含有自我修改的代码,在修改完代码后立即使用ISB指令,以保证随后的指令执行的是新程序
- 内存映射切换:如果系统包含内存映射切换的机制,在程序里切换完内存映射后使用DSB指令,以确保随后的指令用的是更新后的内存映射。
- 动态异常优先级变更:当一个异常处于挂起或激活状态而必须要修改其优先级时,使用DSB指令在修改完之后,以保证在DSB指令完成后所作的修改生效。
- 在多主控系统使用信号量:假如系统包含不止一个总线主控,如果其他处理器在系统中在用,那么每一个处理器都必须在信号量指令之后使用一个DMB指令来确保其他总线的主控能够看到内存事务按照被执行的顺序排列。
对Strongly-ordered区域的内存访问,比如SCB,不需要使用DMB指令。
对于MPU编程,使用一个DSB指令后跟着ISB指令或异常返回,以确保新的MPU配置已被随后的指令使用。
5. bit-banding, 位段
- 在别名(alias)区域写一个词会更新1个位在位段(bit-band)区域
- alias区域的BIT[0]与bit-band区域的位捆绑
- alias区域的BIT[31:1]对相应的bit-band区域没有影响,写0x01和写0xff效果一样,写0x00和写0xfe效果也一样。
- 读取alias区域:0x00000001表示对应bit-band区域的位为1;0x00000000表示该位是0
6. 内存的字节顺序
处理器将内存视为一个线性从0开始递增的字节集合。比如,字节0~3是第1个存储的字;字节4~7是第2个存储的字。
小头格式:
7. 同步原语
Cortex-M4指令集包含成对的同步原语。它们提供了一种非阻塞机制,线程或进程可以使用这种机制获得对内存位置的独占访问。软件可以使用它们来执行一个有保证的读-修改-写内存更新序列,或者用于信号量机制。
一对同步原语包括:
- 一种负载独占指令:用于读取内存位置的值,请求对该位置的独占访问。
- 一种存储独占指令:用于尝试写入相同的内存位置,向寄存器返回一个状态位。如果这位是:
- 0:线程或进程获得对内存的独占访问,写操作成功
- 1:线程或进程没有获得对内存的独占访问,并且没有执行写操作。
负载独占与存储独占指令对为:
- 单词指令LDREX和STREX // Load and store register exclusive.
- 半字指令LDREXH和STREXH
- 字节指令LDREXB和STREXB
软件必须使用一个与Store-Exclusive指令相对应的Load-Exclusive指令。
要执行一个有保证的 读-修改-写 内存位置,软件必须:
- 使用负载独占指令读取位置的值。
- 根据需要更新值。
- 使用存储独占指令尝试将新值写回内存位置。
- 测试返回的状态位。如果这部分是:
- 0:读-修改-写成功完成,
-
1:没有进行写操作。这表明在第1步返回的值可能已经过期。软件必须重试读-修改-写序列,
软件可以使用同步原语实现如下信号量:
- 使用负载独占指令从信号量地址读取信号量,以检查信号量是否空闲。
- 如果信号量是空闲的,则使用存储独占将声称的值写入信号量地址。
- 如果从第2步返回的状态位表明存储独占成功,那么软件就已经获得了信号量。但是,如果存储独占失败,那么在软件执行第1步之后,另一个进程可能已经获得了信号量。
Cortex-M4包含一个独占访问监视器,它标记处理器执行了一个负载独占指令的事实。
如果处理器是多处理器系统的一部分,则系统还全局标记由每个处理器的独占访问寻址的内存位置。
处理器删除它的独占访问标记,如果:
- 它执行一条CLREX指令
- 不管写操作是否成功,它都执行一个存储独占指令。
- 发生异常。这意味着处理器可以解决不同线程之间的信号量冲突。
在多处理器实现中,执行一个:
- CLREX指令只删除处理器的本地独占访问标记
-
存储独占指令或异常。删除处理器的本地独占访问标记和全局独占访问标记。