STM32H742循环中为何需要DSB屏障指令?
在使用STMCubeIDE、编译器优化级别设为-Ofast的环境下,编写了一段将STM32H742的FDCAN Rx FIFO数据复制到普通RAM的循环代码。若不添加__DSB()指令,循环会直接挂起;用500个NOP循环或添加UART输出等额外代码也能解决问题,但最终版本需尽可能精简。请问为何必须添加这个屏障指令?
代码示例:
uint8_t * block_data; char * buf_ptr_ch; uint32_t block_byte_idx; uint32_t byte_cnt_total; // 初始化变量,尤其让buf_ptr_ch指向FDCAN内存区域 while (byte_cnt_total > 0) { block_data [block_byte_idx] = *buf_ptr_ch; block_byte_idx++; byte_cnt_total--; buf_ptr_ch++; __DSB (); }
原因分析
-Ofast优化引发的内存访问乱序-Ofast是GCC的最高级优化选项,会完全忽略严格内存模型规则,允许编译器对内存访问操作进行重排、合并甚至删除。FDCAN Rx FIFO属于外设专用内存区域,这类区域的访问有严格时序要求,不能与普通RAM读写操作乱序执行。无__DSB()时,编译器可能将FDCAN读操作与普通RAM写操作重排,甚至批量读取FDCAN内存后一次性写入RAM,导致CPU在FDCAN数据未就绪时发起访问,或打乱外设内部状态,最终触发挂起。STM32H7的Cache与总线架构特性
STM32H742带有多级Cache(I-Cache、D-Cache),采用AHB/AXI总线架构,外设内存与普通RAM分属不同总线域。访问FDCAN这类外设区域时,需确保总线事务完成后再执行后续操作:__DSB()(数据同步屏障)指令会强制等待所有未完成的内存访问事务执行完毕,确保FDCAN FIFO的读操作确实完成、数据同步到总线后,再执行下一轮循环的地址递增与读写操作。而NOP循环或UART输出只是通过引入延迟让总线事务有时间完成,效率远低于__DSB()。FDCAN外设的访问时序要求
FDCAN Rx FIFO是有状态的外设区域,每次读取后需等待外设更新FIFO指针或状态位。若CPU在一次读操作未完成时就发起下一次访问,可能导致FDCAN外设进入异常状态(如指针混乱),最终造成系统挂起。__DSB()确保每次读操作完成后才进入下一轮循环,符合FDCAN的访问时序要求。
内容的提问来源于Stack Exchange,提问作者Martin_from_K

