ARM基础(4):L1 Cache之I-Cache和D-cache详解

news2024/11/15 0:21:32

在上一篇文章ARM基础(3):MPU内存保护单元详解及例子中,我介绍了MPU,我们知道MPU允许按区域修改一级Cache的属性,这个Cache一般为L1 Cache,它位于CPU的内部,用来加快指令和数据的访问速度。同时,CPU在处理共享数据时需要确保CPU和主存之间的数据一致性。这篇文章就来详细介绍一下L1 Cache的概念和用法。

文章目录

  • 1 L1 Cache和L2 Cache的概念
  • 2 Cortex-M7中的L1 Cache
    • 2.1 缓存行
    • 2.2 缓存的命中和替换
    • 2.3 RT1170的L1 Cache
  • 3 缓存的相关函数
  • 4 如何保证Cache的数据一致性
    • 4.1 例:播放Flash中的音频
    • 4.2 使用cacheable buffer
    • 4.3 使用non-cacheable buffer

1 L1 Cache和L2 Cache的概念

(1)L1 Cache
在ARM体系结构中,L1(Level 1)缓存是位于CPU内部的第一级高速缓存,用于存储指令和数据。L1缓存被进一步分为指令缓存(I-Cache)和数据缓存(D-cache),它们分别专门用于存储指令和数据。I-Cache存储CPU执行的指令,而D-cache存储CPU读取和写入的数据。

I-CacheD-cache的目的是通过提供更快的数据访问速度来减少对主内存的访问。当处理器核心需要执行指令时,它会首先在I-Cache中查找,如果指令已经缓存在I-Cache中,则可以立即执行。同样,当处理器核心需要读取或写入数据时,它会首先在D-cache中查找,如果数据已经缓存在D-cache中,则可以快速访问。
(2)L2 Cache
L2缓存位于CPU和主存之间,作为第二级高速缓存。它的容量比L1缓存更大,可以存储更多的数据,L2缓存是可共享的。它的存在是为了进一步提高缓存命中率和整体性能,以及减少对主存的访问延迟。

  • 与L2缓存相比,L1缓存更接近处理器核心,因此具有更低的访问延迟和更高的带宽,但是实际上L2的访问速度也不低。

2 Cortex-M7中的L1 Cache

2.1 缓存行

Cortex-M7处理器上的L1缓存被分成了32字节的line,每个line都有一个地址标记。即每个缓存行的大小为32字节,缓存行是L1 Cache缓存的基本单位。其中,D-Cache是4路组相联的(每个缓存组可以存储四个缓存行,每个缓存行都有一个标记,用于指示该缓存行在主存中的位置),每组有四个line,而I-Cache则是2路组相联的。较大的缓存会增加成本,较高的组相联度可以提高命中率,但也会增加成本和复杂度,所以D-Cache设置为4路和I-Cache设置为2路是一种硬件上的权衡。

2.2 缓存的命中和替换

如果缓存命中,则数据将从缓存中读取,或者被用于更新到主存中。如果缓存未命中,则会分配并标记一个新的缓存行,并将读或写的数据填充到缓存中。如果所有的缓存行都被分配了,缓存控制器就会运行缓存行替换过程,即选择一行进行清理,并重新分配。D-CacheI-Cache实现了一种伪随机替换算法,用于选择被替换的缓存行。这种算法可以在缓存行的替换过程中提高随机性,从而有效地利用缓存空间,提高缓存命中率。

2.3 RT1170的L1 Cache

在Cortex-M7中,L1 Cache是与CPU的AXI(Advanced eXtensible Interface)总线相连的,从内存中取数据或者写数据到内存中都是依靠这个总线实现的。以I.MX RT1170为例,它是一个双核的CPU,对于CM7来说,有一个32KB的I-Cache和一个32KB的D-cache,它的连接关系如下图所示:
在这里插入图片描述
可以看到在RT1176中有两个紧耦合内存ITCMDTCM,它的访问无需经过L1 Cache,速度非常快,所以建议将临界区的代码和数据放在TCM中,如向量表。需要注意的是,TCM存储器始终是non-Cacheablenon-Shareable的,而不管MPU如何设置。

3 缓存的相关函数

缓存操作的相关函数都是基于CMSIS开发标准的,函数定义在core_cm7.h中,如下表所示:

CMSIS functionDescription
void SCB_EnableICache (void)Invalidate and then enable the instruction cache
void SCB_DisableICache (void)Disable the instruction cache and invalidate its contents
void SCB_InvalidateICache (void)Invalidate the instruction cache
void SCB_EnableDCache (void)Invalidate and then enable the data cache
void SCB_DisableDCache (void)Disable the data cache and then clean and invalidate its contents
void SCB_InvalidateDCache (void)Invalidate the data cache
void SCB_CleanDCache (void)Clean the data cache
void SCB_CleanInvalidateDCache (void)Clean and invalidate the data cache
  • Cache clean:将带有dirty标识的缓存行写入内存中,可以理解为flush
  • Invalidate cache:将缓存中的所有有效数据标记为无效,这意味着下次访问这些数据时,系统将不会从缓存中读取,而是从主存或其他更低级别的缓存中获取最新的数据。失效缓存可以保证读取到最新的数据,特别是当其他设备或处理器修改了存储区域中的数据时。

用户只需要在MPU中设置不同内存区域的属性,然后通过上面列出的CMSIS功能启用缓存即可。例如,用户可以配置MPU是使用write-back还是write-through来对缓存进行操作。

  • write-back:在完成clean操作之前,cache中的数据不会写入到主存中
  • write-through:一旦缓存行上的内容被写入,就更新到主存中。这对于数据一致性来说更安全,但需要更多的总线访问。
    • 实际上,write-through只有很小的影响,除非同一个缓存集被重复且非常快速地访问。所以用哪一种方式是一种权衡。
  • write-alloction:当缓存未命中时,需要为写入和读取操作都分配新的缓存行。

4 如何保证Cache的数据一致性

Cache可以给CPU性能带来一个很大的提升,但是用户应该注意缓存的维护和数据一致性。

4.1 例:播放Flash中的音频

假设我们想播放存储在外部Flash中的音频文件。数据流程图和连接关系如下:
在这里插入图片描述
CPU通过L1 D-Cache读取SRC缓冲区中的音频文件内容,并解码PCM(Pulse Code Modulation)帧数据,写入OCRAM的USER缓冲区。用户缓冲区满后,eDMA开始将PCM帧数据复制到SAI IP模块内的FIFO。然后SAI使用移位操作将FIFO数据移到SAI总线进行音频回放。当CPU将帧数据写入启用L1缓存的OCRAM时,由于OCRAM的默认缓存策略为write-back,因此数据可能只写入cache。eDMA向SAI FIFO传输的数据不正确,导致数据一致性问题。有几种解决方法来解决这个问题:

  1. CPU将数据写入OCRAM后,执行D-Cache清理操作
  2. 在写操作开始前,将OCRAM内存区域缓存策略从write-back设置为write-through
  3. 配置OCRAM内存区域缓存策略为non-cacheable
  4. 配置OCRAM内存区域为shareable,这会固定cache策略为non-cacheable

4.2 使用cacheable buffer

对于定义在OCRAM,SRAM的buffer,一般都为Cacheable且Cache策略为write-back。如果要将定义在这里面的buffer作为DMA的源,用户必须在DMA开启之前执行一个D-Cache的清除操作,即SCB_CleanDCache();如果该buffer用作DMA的目标,在DMA完成后且CPU或其它主机读取数据之前,需要执行SCB_InvalidateDCache

  • buffer的地址应该基于L1缓存行的大小进行对齐,在Cortex-M7中为32字节

4.3 使用non-cacheable buffer

使用定义在non-cacheable内存区域的buffer直接解决了数据的一致性的问题,但是由于没有使用cache,这会导致访问这个buffer的速度会变慢很多。

最后以MCUXPresso IDE为例,看看如何定义一个non-cacheable buffer
(1)在链接脚本中添加non-cacheable的内存段
对于MCUXPresso IDE来说,可以直接在GUI中修改:

①在Memory details中添加NCACHE_REGION
在这里插入图片描述
②在链接脚本中添加input section
在这里插入图片描述
(2)MPU配置
对于DTCM来说,由于16组MPU的配置可以按优先级来覆盖。所以上面的代码我们先配置DTCM的cache策略默认为Write-back, no write allocate,代码如下:

    MPU->RBAR = ARM_MPU_RBAR(5, 0x20000000U);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_256KB);

接着我们要配置一下non-cacheable区域的MPU,代码如下:

extern uint32_t __base_NCACHE_REGION;
extern uint32_t __top_NCACHE_REGION;
uint32_t nonCacheStart = (uint32_t)(&__base_NCACHE_REGION);
uint32_t size          = (uint32_t)(&__top_NCACHE_REGION) - nonCacheStart;
volatile uint32_t i = 0;

while ((size >> i) > 0x1U)
{
    i++;
}

if (i != 0)
{
    /* The MPU region size should be 2^N, 5<=N<=32, region base should be multiples of size. */
    assert(!(nonCacheStart % size));
    assert(size == (uint32_t)(1 << i));
    assert(i >= 5);

    /* Region 10 setting: Memory with Normal type, not shareable, non-cacheable */
    MPU->RBAR = ARM_MPU_RBAR(10, nonCacheStart);
    MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 1, 0, 0, 0, 0, i - 1);
}

首先我们来看一下链接脚本文件evkmimxrt1170_freertos_hello_cm7_Debug_memory.ld中的相关定义:

  __base_NCACHE_REGION = 0x20300000  ; /* NCACHE_REGION */  
  __base_RAM5 = 0x20300000 ; /* RAM5 */  
  __top_NCACHE_REGION = 0x20300000 + 0x40000 ; /* 256K bytes */  

C语言中可以直接用extern获取在链接脚本中定义的变量,所以__base_NCACHE_REGION __top_NCACHE_REGION实际上就对应我们刚刚在Memory details中添加的NCACHE_REGION。然后前面的代码就是找到一个最小的整数i,使得2^i大于等于变量size,然后以此就可以对参数进行合法性判断:整个区域的大小是不是2的倍数、是否大小大于一个缓存行等。同时ARM_MPU_RASR最后一个参数,内存区域的大小正好就可以用i-1来表示为2^i字节。

最后就是调用ARM_MPU_RASR配置MPU了,其中TEX为1、CB为0,表示这段内存的类型为Normal,cache策略为non-cacheable

(3)定义变量并链接到NCACHE_REGION
我们可以使用__attribute(section())关键字来指定变量链接到哪个段,MCUXPresso IDE中提供了这个宏:

#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) \
    __attribute__((section("NonCacheable,\"aw\",%nobits @"))) var __attribute__((aligned(alignbytes)))

其中nobits表示该段为bss段,无需写入Flash。这里还加了字节对齐的属性,一般我们是4字节对齐,但有一些特殊的用例就有特殊的字节对齐的要求,比如eLCDIFframebuffer需要8字节对齐。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/635537.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:如何在Python和Java之间进行整合

如何在Python和Java之间进行整合 在现在这个编程的时代里&#xff0c;使用一种编程语言或一种技术通常不能满足所有的需求。因此&#xff0c;当你需要在Python和Java之间进行整合时&#xff0c;这篇文章就为你介绍了一些解决方案。 为什么需要Python和Java进行整合 Python和…

redis 字典的实现

1.数据结构 节点数据结构 因为是基于开链法的哈希表实现&#xff0c;所以需要维护了一个next节点 typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next; } dictEntry; 复制 哈希表数据结构 其中size是当…

景区旅游多商户版小程序v14.3.1+前端

&#x1f388; 限时活动领体验会员&#xff1a;可下载程序网创项目短视频素材 &#x1f388; &#x1f389; 有需要的朋友记得关赞评&#xff0c;文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 【新增】全新授权登录支持取消登录 【新增】商…

3.12生产者消费者模型 3.13条件变量 3.14信号量 C++实现生产者消费者模型

3.12生产者消费者模型 生产者消费者模型中的对象&#xff1a; 1、生产者 2、消费者 3、容器 若容器已满&#xff0c;生产者阻塞在这&#xff0c;通知消费者去消费&#xff1b;若容器已空&#xff0c;则消费者阻塞&#xff0c;通知生产者去生产。生产者可以有多个&#xff0c;消…

RK3588平台开发系列讲解(USB篇)Linux Android USB软件架构

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、高通平台USB软件架构二、USB 设备侧软件组成2.1、OTG driver2.2、Device controller driver2.3、Gadget framework2.4、Function drivers三、USB 主机侧软件3.1、Host controller driver3.2、USB core3.3、USB PHY…

chatgpt赋能python:Python取消断点的方法

Python取消断点的方法 在Python开发过程中&#xff0c;我们经常需要设置断点来帮助我们调试代码。但是&#xff0c;有时候我们可能需要取消已设置的断点。本文将介绍如何取消Python中的断点。 1. 什么是断点 在Python中&#xff0c;断点是为了帮助调试代码而设置的一个标志。…

网络层:IPv4地址

网络层&#xff1a;IPv4地址 笔记来源&#xff1a; 湖科大教书匠&#xff1a;IPv4地址概述 湖科大教书匠&#xff1a;分类编址的IPv4地址 湖科大教书匠&#xff1a;划分子网的IPv4地址 湖科大教书匠&#xff1a;无分类编址的IPv4地址 IPv4地址就是给因特网(Internet)上的每一…

零售场景梳理和运筹优化工作经验总结

文章目录 亡羊补牢不为迟零售行业规模大卷出零售新高度运筹优化实践经验 亡羊补牢不为迟 由于工作岗位变动的缘故&#xff0c;暂时要告别零售场景了。当初自己没想太多就一头扎进了“新”零售这个场景&#xff0c;迄今为止都没有针对零售场景做一个通盘的梳理&#xff0c;现在…

御2pro,带屏遥控器航测设置

御2遥控器航测设置 0 前言1 遥控器设置2 航测软件设置3 航测设置 0 前言 无人机&#xff1a;御2 或者御2 pro&#xff0c;非变焦版本遥控器&#xff1a;大疆带屏控器 RM500 1 遥控器设置 默认的遥控器控制软件CONNECT只能航拍&#xff0c;无法航测&#xff0c;必须调节为航…

Docker常用基本命令

一、docker的基础命令 1、启动docker systemctl start docker 2、关闭docker systemctl stop docker 3、重启docker systemctl restart docker 4、设置docker开机自启动 systemctl enable docker 5 &#xff0c; 查看docker运行状态&#xff08;显示绿色代表正常启动…

类和对象(再谈构造函数)

文章目录 1.再谈构造函数1.1构造函数的赋值1.2初始化列表1.3 explicit关键字 2. static成员2.1概念2.2特性2.3例题 3 .友元3.1 友元函数3.2友元类 4.内部类5.匿名对象6.拷贝对象时编译器的一些优化 1.再谈构造函数 1.1构造函数的赋值 在创建对象时&#xff0c;编译器通过调用…

使用自签发CA证书为EMQX开启双向认证

文章目录 背景信息1、CA证书信任模型2、创建证书2.1 Root CA 证书创建2.2 emqx 服务端证书签发2.3 中间CA证书签发2.4 设备证书签发 3、配置EMQX服务端证书4、客户端使用TLS连接EMQX 背景信息 本文主要介绍了通过建立三层CA证书链&#xff0c;为EMQX集群提供PKI服务&#xff0c…

STM32基于库函数新建工程模板

基于库函数版本 准备资料&#xff1a; a) V3.5 固件库包&#xff1a;STM32F10x_StdPeriph_Lib_V3.5.0 这是 ST 官网下载的固件库完 整版&#xff0c;我们光盘目录&#xff1a; 软件资料\STM32 固件库使用参考资料\STM32F10x_StdPeriph_Lib_V3.5.0 我们官方论坛下载地址&#…

C++入门(嵌入式学习)

C入门学习 前言C概述C和C的区别 引用引用概念引用性质引用的应用 C输入和赋值string字符串函数提高函数的默认值默认值的注意事项函数的重载函数重载可能产生的问题扩充 内联函数 前言 C概述 C是一种通用的高级编程语言&#xff0c;它是由Bjarne Stroustrup于20世纪80年代初在…

论文笔记:MEASURING DISENTANGLEMENT: A REVIEW OF METRICS

0 摘要 学习解缠和表示数据中的变化因素是人工智能中的一个重要问题。虽然已经取得了许多关于学习这些表示的进展&#xff0c;但如何量化解缠仍然不清楚。 虽然存在一些度量标准&#xff0c;但对它们的隐含假设、真正衡量的内容以及限制了解甚少。因此&#xff0c;当比较不同的…

睡眠脑电中的神经跨频率耦合函数

导读 人类大脑是一个紧密连接的复杂系统。虽然其结构比较固定&#xff0c;但它可以实现很多不同的功能。其中一个重要的功能是自然睡眠过程&#xff0c;这个过程可以改变意识和随意肌肉活动。在神经层面上&#xff0c;这些改变会伴随着大脑连接的变化。为了揭示这种与睡眠相关…

推荐一个好用的开发工具百宝箱

随着科技不断发展&#xff0c;越来越多的工具在网络上应运而生&#xff0c;方便我们更加高效地完成各种任务。今天我要向大家介绍一款在线工具——码加在线工具 - 做更好的工具&#xff0c;它可以帮助你轻松完成许多繁琐、复杂的工作。 首先&#xff0c;码加在线工具 是一款非…

一维信号进行小波去噪(python)

目录 小波变换小波去噪的原理小波阈值去噪的三个主要方面pywt.threshold函数进行小波去噪对ecg信号进行小波阈值去噪关于阈值输出参考 小波变换 小波变换是一种信号的时间——尺度&#xff08;时间——频率&#xff09;分析方法&#xff0c;它具有多分辨分析的特点&#xff0c…

【模型评估】ROC(Receiver operating characteristic)与 AUC

前面&#xff0c;我们提到了混淆矩阵&#xff0c;以及根据混淆矩阵进一步计算得到的敏感度&#xff08;召回率&#xff09;、特异度、精确度、准确度、F1 Score等等。那他们的前提都是要首先确定一个截断阈值。 【模型评估】混淆矩阵&#xff08;confusion_matrix&#xff09;…

理论粘贴板-背会了避免在大佬面前露馅-常更新

1.OLS说明 最小二乘法。给定序列X(x1,x2…xn),y,估计一个向量A(a0,a1.a2…)令y’a0a1x1a2x2…an*xn, 使得(y’-y)^2最小&#xff0c;计算A。 2.代码如下 来源《python机器学习实践指南》 import patsy import statsmodels.api as sm f ‘Rent ~ Zip Beds’ y, X patsy.dmat…