ARM Cortex-M 系列处理器中的 __dsb()
和 __isb()
是内存屏障指令,用于确保内存操作的顺序性和可见性,尤其在涉及外设、多核/多线程、自修改代码或关键系统配置时至关重要。
一,详细说明和典型应用场景
1. __dsb()
(Data Synchronization Barrier)
- 作用:确保所有在它之前的内存访问指令(如加载/存储)完成,后续指令(不限于内存操作)必须等待此屏障执行完毕。
- 参数:通常指定作用域(如
DSB_SY
/0xF
表示全系统屏障)。 - 典型场景:
- 外设寄存器操作:修改硬件寄存器后,需确保写入完成再继续。
*((volatile uint32_t*)0x40000000) = 0x1234; // 写入外设寄存器 __dsb(0xF); // 等待写入完成
- DMA 配置:启动 DMA 前,确保数据已写入内存。
- 内存保护单元(MPU)配置:修改 MPU 寄存器后,需同步确保配置生效。
- 自修改代码:写入新代码后,确保数据同步再执行 ISB。
- 外设寄存器操作:修改硬件寄存器后,需确保写入完成再继续。
2. __isb()
(Instruction Synchronization Barrier)
- 作用:清空处理器流水线,确保后续指令从内存重新读取,且所有之前的屏障指令(如 DSB)已生效。
- 参数:通常为
ISB_SY
/0xF
。 - 典型场景:
- 系统控制寄存器修改:如修改异常向量表基址(VTOR)后。
SCB->VTOR = 0x20000000; // 设置新向量表地址 __dsb(0xF); // 确保写入完成 __isb(0xF); // 清空流水线,后续中断使用新向量表
- 跳转到动态代码:执行新生成的代码前,确保指令更新。
- 安全状态切换:如从非安全状态切换到安全状态后。
- 系统控制寄存器修改:如修改异常向量表基址(VTOR)后。
3. 联合使用场景
在关键系统配置中,通常需先 DSB
再 ISB
,确保数据同步和指令重新取指:
// 示例:修改 MPU 配置
MPU->RBAR = ...; // 写入 MPU 寄存器
MPU->RASR = ...;
__dsb(0xF); // 等待配置写入完成
__isb(0xF); // 清空流水线,确保后续指令使用新配置
4. 区别总结
指令 | 作用对象 | 关键行为 | 典型场景 |
---|---|---|---|
__dsb() |
内存访问 | 等待数据访问完成 | 外设操作、DMA、MPU 配置 |
__isb() |
指令流水线 | 清空流水线并重新取指 | 修改 VTOR、执行新代码、特权切换 |
5. 注意事项
- 编译器屏障:
__dsb()
/__isb()
是硬件级屏障,不同于__asm volatile("" ::: "memory")
(仅限制编译器重排)。 - 性能影响:过度使用屏障会降低性能,仅在必要时使用。
- ARMv7-M/Armv8-M 文档:具体行为参考 ARM Architecture Reference Manual。
通过合理使用这两种屏障,可确保代码在 Cortex-M 处理器上的正确性和可靠性。
二,内存屏障指令会被中断打断吗?
在 ARM Cortex-M 系列处理器中,内存屏障指令(如 __dsb()
和 __isb()
)本身不会被中断打断。以下是详细分析和关键结论:
1. 内存屏障指令的原子性
ARM 处理器在设计上保证单条指令的原子性。当处理器执行一条指令时,会确保该指令完全执行完毕后再响应中断。因此:
__dsb()
和__isb()
作为单条指令,执行过程中不会被中断打断。- 中断的触发和响应发生在当前指令执行完成之后。
2. 中断对屏障效果的影响
虽然屏障指令本身不会被中断打断,但中断处理函数的执行可能影响屏障的预期效果,需注意以下场景:
(1) 关键代码段的保护
- 场景:在修改系统关键配置(如 MPU、VTOR)或外设寄存器后,若未完成屏障操作即被中断,中断处理函数可能访问到不一致的状态。
- 解决方案:
// 示例:修改 VTOR 前关闭中断 __disable_irq(); // 关闭全局中断 SCB->VTOR = 0x20000000; // 设置新向量表地址 __dsb(0xF); // 确保写入完成 __isb(0xF); // 清空流水线 __enable_irq(); // 重新启用中断
(2) 中断处理中的内存访问
- 若屏障指令后发生中断,中断处理函数中的代码默认不继承屏障的同步效果。
- 若中断处理函数需要依赖屏障前的内存操作,需在中断处理函数中显式使用屏障。
3. 屏障指令与中断的时序关系
__dsb()
的作用:确保此前所有内存访问完成。- 若在
__dsb()
后发生中断,中断处理函数能看到屏障前的内存修改。
- 若在
__isb()
的作用:清空流水线,确保后续指令重新取指。- 若在
__isb()
后发生中断,中断处理函数会从新指令流开始执行(例如修改后的代码或配置)。
- 若在
4. 总结
关键点 | 说明 |
---|---|
指令原子性 | 单条屏障指令不会被中断打断,确保自身操作完整执行。 |
中断对屏障的影响 | 中断处理函数可能破坏屏障的上下文一致性,需通过关闭中断或显式同步规避。 |
典型场景 | 修改关键硬件配置、动态代码加载、多核共享资源访问等。 |
5. 最佳实践
在关键代码段关闭中断:
修改系统关键状态(如 MPU、VTOR)时,先禁用中断,执行屏障后再启用。__disable_irq(); // 修改关键配置 __dsb(0xF); __isb(0xF); __enable_irq();
中断处理函数中显式同步:
若中断处理依赖屏障前的操作,需在中断处理函数内插入屏障。void ISR_Handler() { __dsb(0xF); // 确保看到屏障前的内存修改 // 处理逻辑 }
避免过度使用屏障:
仅在必要时使用屏障,以减少对实时性的影响。
通过合理设计中断与屏障的交互,可确保 Cortex-M 系统的强实时性和数据一致性。