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

相关文章

PHP应用中处理限流和API节流的最佳实践

《PHP应用中处理限流和API节流的最佳实践》限流和API节流对于确保Web应用程序的可靠性、安全性和可扩展性至关重要,本文将详细介绍PHP应用中处理限流和API节流的最佳实践,下面就来和小编一起学习... 目录限流的重要性在 php 中实施限流的最佳实践使用集中式存储进行状态管理(如 Redis)采用滑动

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

Python异步编程之await与asyncio基本用法详解

《Python异步编程之await与asyncio基本用法详解》在Python中,await和asyncio是异步编程的核心工具,用于高效处理I/O密集型任务(如网络请求、文件读写、数据库操作等),接... 目录一、核心概念二、使用场景三、基本用法1. 定义协程2. 运行协程3. 并发执行多个任务四、关键