ARM内存屏障/编译屏障API(__DMB、__DSB、__ISB)用法及举例

2024-08-31 14:44

本文主要是介绍ARM内存屏障/编译屏障API(__DMB、__DSB、__ISB)用法及举例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

0 参考资料

STM32F7 Series and STM32H7 Series Cortex®-M7 processor.pdf
ARM Cortex™-M Programming Guide to Memory Barrier Instructions.pdf

1 ARM内存屏障/编译屏障指令(__DMB、__DSB、__ISB)说明

内存屏障和编译屏蔽其实是2个东西,一个是处理器运行时避免乱序引入的指令,一个是编译时避免编译器编译后的代码乱序引入的指令。它们二者具有什么样的联系呢,为什么在嵌入式开发中常见的__DMB、__DSB、__ISB API将这二者都结合在一起了呢?下面一一道来。
在进行嵌入式开发中,经常会看到DMB、DSB、ISB这三个指令。在具有超标量流水线的MCU(如Cortex-M7内核的stm32H743)/SoC更是经常会使用到,相对于只支持三级流水线的Cortex-M4在每个时钟周期只能执行一条指令,Cortex-M7拥有六级流水线和双发射超标量架构,拥有在一个时钟周期内执行两条指令的能力。以五级指令流水线为例,其指令执行步骤如下:

取指->译码->发射->执行->写回

其中发射和执行都是乱序的。
超标量流水线的设计产生了乱序指为嵌入式开发带来新的问题,同时固有的编译器优化也会导致内存乱序访问。总结来说会带来以下2个问题:
(1)编译时,编译器优化导致指令执行步骤和实际书写的顺序不一致,进而产生内存乱序访问。为了解决这一问题,可以使用"memory"这个伪指令,告诉编译器不要将该指令前后代码顺序打乱。
(2)运行时,由于处理器乱序执行导致内存乱序访问
DMB、DSB、ISB就是为了解决这一问题而引入的指令。

为了解决上述的2个问题,可以直接将内存屏障和编译屏障结合在一起使用。以基于Cortex-A7的stm32MP135为例,其__DMB、__DSB、__ISB的API如下:

/**\brief   Instruction Synchronization Barrier\details Instruction Synchronization Barrier flushes the pipeline in the processor,so that all instructions following the ISB are fetched from cache or memory,after the instruction has been completed.*/
__STATIC_FORCEINLINE  void __ISB(void)
{__ASM volatile ("isb 0xF":::"memory");
}/**\brief   Data Synchronization Barrier\details Acts as a special kind of Data Memory Barrier.It completes when all explicit memory accesses before this instruction complete.*/
__STATIC_FORCEINLINE  void __DSB(void)
{__ASM volatile ("dsb 0xF":::"memory");
}/**\brief   Data Memory Barrier\details Ensures the apparent order of the explicit memory operations beforeand after the instruction, without ensuring their completion.*/
__STATIC_FORCEINLINE  void __DMB(void)
{__ASM volatile ("dmb 0xF":::"memory");
}

可以看到每个指令既包含和自己相关的指令,也包含"memory"这个伪指令,这是ARM平台的编译屏障指令。

1.1 DMB(Data Memory Barrier,数据内存屏障)指令

DMB主要用于多核处理器系统中,不同的处理器可能同时执行数据内存传输指令。DMB指令确保在DMB之前的所有显式数据内存传输指令都已经在内存中读取或写入完成,同时确保任何后续的数据内存传输指令都将在DMB执行之后开始执行,否则有些数据传输指令可能会提前执行。
它保证的是DMB之前的内存访问指令与DMB之后的内存访问指令的执行顺序,DMB不保证内存访问的完成顺序(保执行,不保完成)。也就是说,DMB指令之后的内存访问指令不会被处理器重排到DMB指令的前面。DMB指令不会保证内存访问指令在内存屏障指令之前完成,它仅仅保证内存屏障指令前后的内存访问的执行顺序。DMB指令只影响内存访问指令、数据cache指令以及cache管理指令等,并不会影响其他指令(例如算术运算指令)的执行顺序。
注意:这里所说的多核不特指具有多个核心的CPU,如对多个不同区域的内存空间进行操作,如对DDR和系统寄存器。

1.2 DSB(Data Synchronization Barrier,数据同步屏障)指令

在计算机的体系结构中,处理器在执行指令时通常会利用指令流水线来提高性能。但也会产生一些问题,比如在多线程编程中,两个线程同时对共享的内存进行读写操作,由于读/写操作的重排序,就会导致数据的不一致。
当执行DSB指令时,它确保在DSB之前的所有显式数据内存传输指令都已经在内存中读取或写入完成,同时确保任何后续的指令都将在DSB执行之后开始执行。
DSB比DMB指令严格一些,仅当所有在它前面的内存访问指令都完成后,才会执行在它后面的指令,即任何指令都要等待DSB指令前面的内存访问指令完成。位于此指令前的所有缓存(如分支预测和TLB维护)操作需要全部完成。

注意:设备内存(Device Memory)/强序内存(Strongly Ordered Memory)类型访问时自动添加数据同步屏障DSB,不需要再自行添加

1.3 ISB(Instruction Synchronization Barrier,指令同步屏障)指令

指令的流水线允许处理器同时执行多条指令的不同阶段,然而这样并行执行可能会导致一些问题,特别是涉及到上下文切换(更改上下文操作包括cache、TLB、分支预测等维护操作以及改变系统控制寄存器等操作)的情况,如实时操作系统的任务切换。当上下文切换时,可能指令流水线中的指令还在执行,而此时上下文已经改变,导致指令执行的结果不正确。

通过插入ISB指令,处理器会将流水线中的指令全部刷新,从而确保之前的指令不会影响后续指令的执行,并且后续指令将从正确的上下文开始重新获取。
注:大多数CPU的体系架构在异常的入口和出口都有ISB的语义(自动执行)

2 ARM内存屏障指令(__DMB、__DSB、__ISB)在stm32MP135中的使用举例

下面介绍以基于Cortex-A7的stm32MP135为例,查看官方HAL库在哪些地方使用到了内存屏障指令,加深我们对内存屏障指令的认识。

2.1 __DMB使用举例

(1)

if (heth->RxDescList.RxBuildDescCnt != desccount){/* Set the tail pointer index */tailidx = (descidx + 1U) % ETH_RX_DESC_CNT;/* DMB instruction to avoid race condition */__DMB();/* Set the Tail pointer address */WRITE_REG(heth->Instance->DMAC0RXDTPR, ((uint32_t)(heth->Init.RxDesc + (tailidx))));heth->RxDescList.RxBuildDescIdx = descidx;heth->RxDescList.RxBuildDescCnt = desccount;}

__DMB()前一个操作是设置尾指针索引,后一个操作是写通道0 Rx描述符尾指针寄存器。通过__DMB()指令能够确保设置尾指针索引操作完成后再执行写通道0 Rx描述符尾指针寄存器,避免内存乱序执行带来错误的尾指针索引写入。
如果操作的数据属于同一个内存区域,则不需要使用该API,CPU会保证乱序执行后的效果和正常执行顺序的数据操作效果一致。

2.2 __DSB使用举例

(1)

if (heth->gState == HAL_ETH_STATE_STARTED){/* Config DMA Tx descriptor by Tx Packet info */if (ETH_Prepare_Tx_Descriptors(heth, pTxConfig, 0) != HAL_ETH_ERROR_NONE){/* Set the ETH error code */heth->ErrorCode |= HAL_ETH_ERROR_BUSY;return HAL_ERROR;}/* Ensure completion of descriptor preparation before transmission start */__DSB();dmatxdesc = (ETH_DMADescTypeDef *)(&heth->TxDescList)->TxDesc[heth->TxDescList.CurTxDesc];/* Incr current tx desc index */INCR_TX_DESC_INDEX(heth->TxDescList.CurTxDesc, 1U);/* Start transmission *//* issue a poll command to Tx DMA by writing address of next immediate free descriptor */WRITE_REG(heth->Instance->DMAC0TXDTPR, (uint32_t)(heth->TxDescList.TxDesc[heth->TxDescList.CurTxDesc]));/* Return function status */return HAL_OK;}else{return HAL_ERROR;}

__DSB()用于确保传输开始前完成DMA描述符的准备。
如果操作的数据属于同一个内存区域,则不需要使用该API,CPU会保证乱序执行后的效果和正常执行顺序的数据操作效果一致。

2.3 __ISB使用举例

Cortex-A7建议在以下地方使用__ISB指令:
在这里插入图片描述
整个cache操作(如清空、无效化)、TLB(Translation Lookaside Buffer的简称,可翻译为“地址转换后援缓冲器”,也可简称为“快表”)操作、分支预测器操作、更改系统控制寄存器操作。

(1)

__set_TTBR0(((uint32_t)ttb_addr) | 9U);
__ISB();

__ISB()使用之后可以看到TTBR0寄存器的设置效果。TTBR0寄存器是页表基地址寄存器,和TLB相关。
(2)

/** \brief  Invalidate the whole instruction cache
*/
__STATIC_FORCEINLINE void L1C_InvalidateICacheAll(void) {__set_ICIALLU(0);__DSB();     //ensure completion of the invalidation__ISB();     //ensure instruction fetch path sees new I cache state
}

__ISB()之前将整个Cache无效化。ARM推荐的整个cache无效化操作使用ISB指令。
简而言之,__ISB()之后能够看到指令执行以后的效果。例如将整个cache无效化是需要等待时间的,这个__ISB()就相当于等待cache无效化完成。

这篇关于ARM内存屏障/编译屏障API(__DMB、__DSB、__ISB)用法及举例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1124203

相关文章

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

Java中八大包装类举例详解(通俗易懂)

《Java中八大包装类举例详解(通俗易懂)》:本文主要介绍Java中的包装类,包括它们的作用、特点、用途以及如何进行装箱和拆箱,包装类还提供了许多实用方法,如转换、获取基本类型值、比较和类型检测,... 目录一、包装类(Wrapper Class)1、简要介绍2、包装类特点3、包装类用途二、装箱和拆箱1、装

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API

MyBatis-Flex BaseMapper的接口基本用法小结

《MyBatis-FlexBaseMapper的接口基本用法小结》本文主要介绍了MyBatis-FlexBaseMapper的接口基本用法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具... 目录MyBATis-Flex简单介绍特性基础方法INSERT① insert② insertSelec

一分钟带你上手Python调用DeepSeek的API

《一分钟带你上手Python调用DeepSeek的API》最近DeepSeek非常火,作为一枚对前言技术非常关注的程序员来说,自然都想对接DeepSeek的API来体验一把,下面小编就来为大家介绍一下... 目录前言免费体验API-Key申请首次调用API基本概念最小单元推理模型智能体自定义界面总结前言最