STM32H7 的 FMC 外设在 DCACHE 使能时运行不正常

judy的头像
judy 发布于:周三, 11/15/2017 - 10:33 ,关键词:

前言

前段时间接到一个客户的问题。客户描述在使用 STM32H7 的 FMC 时,如果使能了 DCACHE,就运行不正常。数据没有写到 FMC 外部的存储器里,测量 FMC 接口也没有波形跳变。而不使能 DCACHE 工作就是正常的。

其实对于这个问题,如果了解 STM32H7 的架构的话,就很容易理解了。下面我们就来看一看到底是什么原因让客户觉得使能 DCACHE 后 FMC 就工作不正常了。

STM32H7 的架构

下面是 STM32H7 架构图中和 FMC 相关的部分。从图中可以看到,STM32H7 是基于 Cortex-M7 内核,在内部的 Cortex-M7内核里带有一个 16KB 的 DCACHE 和一个 16KB 的 ICACHE。内核通过 AXIM 总线连接到的 64 位的 AXI 总线矩阵,通过这个总线矩阵连接到 FMC 接口。也就是说,当 DCACHE 使能的时候,对 FMC 接口上的存储器的操作是经过内核的缓存(CACHE)的。

STM32H7 的 FMC 外设在 DCACHE 使能 时运行不正常

使能了 CACHE 的好处就是可以提高内核访问存储器的速度。为什么使用缓存能提高速度呢?是因为在操作可以被缓存的存储器的时候,内核不是每次都去直接操作这些存储器,而是从缓存里读取数据,或者将数据先写到缓存里。看到这里,是不是觉得和前面客户说的现象有点联系了。

Cache 的操作

简单的说缓存工作时有两种方式:回写和透写。

STM32H7 的 FMC 外设在 DCACHE 使能 时运行不正常

回写就是数据只写到缓存中,等以后再同步到下一级存储器中。

透写就是数据每次都同时写到缓存和下一级存储器中。这种方式可以解决数据不一致的问题,但同时也会消耗更多的总线访问。

缓存的操作方式,在启用缓存后有一个默认的设置 。也就是下面这张表:

STM32H7 的 FMC 外设在 DCACHE 使能 时运行不正常

请参考 AN4839 了解更多的关于 Cache 的使用说明。

问题分析及解决

客户是通过 FMC 的 NOR 存储控制器外接一个 FPGA。映射的地址范围是 0x60000000~0x7FFFFFFF。从上图可以看到,它默认的缓存操作方式是 WBWA(回写写分配)。也就是说,当打开 DCACHE 后,所有对该地址范围的操作都会先到缓存。

这也解释了为什么客户发现数据没有真正写到外部存储器中。客户的数据需要实时写到 FPGA 中进行计算。所以对这个应用场景,回写的设置就不合适了。

对于客户的这个应用,可以通过 MPU 来进行配置,将 0x60000000~0x7FFFFFFF 范围地址的 CACHE 设置为透写的方式,这样数据就会实时的写到 FPGA 中去了。

针对这种情况,我们可以通过 MPU 来解决这个问题。

通过 MPU,可以配置不同存储器空间的访问权限和 Cache 策略。HAL 库里面提供了对应的函数和例程,参照例程用下面这段代码就可以解决客户的问题了。数据每次都是直接写到 FPGA 中,而不是缓存到内核的 DCACHE 中。

//MPU related define
#define FPGA_ADDRESS_START (0x60000000UL)
#define FPGA_SIZE MPU_REGION_SIZE_64MB
#define FPGA_REGION_NUMBER MPU_REGION_NUMBER0
#define FPGA_ACCESS_PERMISSION MPU_REGION_FULL_ACCESS
void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable MPU */
HAL_MPU_Disable();
/* Configure FPGA region as Region N?, 64MB of size and R/W region */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = FPGA_ADDRESS_START;
MPU_InitStruct.Size = FPGA_SIZE;
MPU_InitStruct.AccessPermission = FPGA_ACCESS_PERMISSION;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = FPGA_REGION_NUMBER;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

总结

CACHE 的使用可以帮我们提高程序运行的性能,但也会带来缓存和二级存储器数据不一致和缓冲不击中带来的程序执行的时间不确定的情况,需要客户根据实际的应用来进行选择。对于实时性要求高的数据,可以像前面一样使用 MPU 配置成透写的方式,或者将数据放到 DTCM RAM 中。另外,在本文中只用 MPU 配置了 FMC 映射的部分存储区域。在实际使用中,应该对所有用到的存储空间(比如 Flash, SRAM 区)的属性根据实际情况进行配置,以免引起其他的问题。

来源: ST

围观 604