启用cache很简单,就是这两句,分别打开I-Cache和D-Cache,但是如果只使用这两句,再操作DMA和FLASH时就很有可能遇到问题,后面会具体说明。
SCB_EnableICache();//使能I-Cache
SCB_EnableDCache();//使能D-Cache
这里是一个RAM的MPU保护配置。
/* Disables the MPU */
HAL_MPU_Disable();
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = D1_AXISRAM_BASE;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
首先是配置MPU时需要先关闭MPU,HAL_MPU_Disable();启用HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);其中的参数是MPU的控制策略
//默认区域(default region)不允许访问,并且在硬故障,NMI和FAULTMASK处理程序期间禁用MPU
#define MPU_HFNMI_PRIVDEF_NONE ((uint32_t)0x00000000)
//默认区域(default region)不允许访问,并且在硬故障,NMI和FAULTMASK处理程序期间启用MPU
#define MPU_HARDFAULT_NMI ((uint32_t)0x00000002)
//默认区域(default region)仅允许特权访问,并且在硬故障,NMI和FAULTMASK处理程序期间禁用MPU
#define MPU_PRIVILEGED_DEFAULT ((uint32_t)0x00000004)
//默认区域(default region)仅允许特权访问,并且在硬故障,NMI和FAULTMASK处理程序期间启用MPU
#define MPU_HFNMI_PRIVDEF ((uint32_t)0x00000006)
第一个配置参数MPU_InitStruct.Enable,可以赋值是为MPU_REGION_ENABLE、MPU_REGION_DISABLE,分别代表这个区域的MPU是否启用。
第二个配置参数MPU_InitStruct.Number,可以赋值MPU_REGION_NUMBER0、MPU_REGION_NUMBER1、MPU_REGION_NUMBER2...一共是8个,代表所配置保护内存区域的编号。
第三个配置参数MPU_InitStruct.BaseAddress,可以赋值的就是各个内存块的首地址,这里的D1_AXISRAM_BASE是0x24000000,是一个512K的RAM。
第四个配置参数MPU_InitStruct.Size,就是所保护内存的大小,MPU_REGION_SIZE_512KB就是要保护区域的大小,还有其他大小可选,具体可以看宏定义中所定义的大小。
第五个配置参数MPU_InitStruct.SubRegionDisable,是配置子区域,每个MPU配置的内存区域都被分为了8个子区域,数值从0~255,每一个bit对应着一个子区域的禁用(1)或者启用(0)。最高位(控制最后一个子区域,最低位控制第一个子区域,最后注意的是大于128字节才可以使用子区域。
第六个配置参数MPU_InitStruct.AccessPermission是配置RAM的读写权限。
/** @defgroup CORTEX_MPU_Region_Permission_Attributes CORTEX MPU Region Permission Attributes
* @{
*/
#define MPU_REGION_NO_ACCESS ((uint8_t)0x00) //禁止所有情况下的访问(读/写)
#define MPU_REGION_PRIV_RW ((uint8_t)0x01) //仅允许在特权模式下读写
#define MPU_REGION_PRIV_RW_URO ((uint8_t)0x02) //特权模式下可读写,用户模式下只读
#define MPU_REGION_FULL_ACCESS ((uint8_t)0x03) //完全访问,特权和用户模式下都可以读写
#define MPU_REGION_PRIV_RO ((uint8_t)0x05) //仅允许在特权模式下读取
#define MPU_REGION_PRIV_RO_URO ((uint8_t)0x06) //特权和用户模式下都只读
第七个配置参数MPU_InitStruct.DisableExec,表示代码是否在RAM中可执行,MPU_INSTRUCTION_ACCESS_ENABLE代表可执行。
最后的4个参数需要一起看,
MPU_InitStruct.TypeExtField;MPU_InitStruct.IsShareable;MPU_InitStruct.IsCacheable;MPU_InitStruct.IsBufferable
TypeExtField |
IsCacheable | IsBufferable | IsShareable | cache功能 |
MPU_TEX_LEVEL0 | 1 | 0 | x | Write through, no write allocate |
MPU_TEX_LEVEL0 | 1 | 1 | x | Write back, no write allocate |
MPU_TEX_LEVEL1 | 0 | 0 | x | Non-cacheable |
MPU_TEX_LEVEL1 | 1 | 1 | x | Write back, write and read allocate |
1.Write through, no write allocate(透写)
(1)写入策略:如果cache命中,则同时向cache和对应的内存区域中写入;如果未命中,则只写入对应的内存区域,不开辟新的cache。
(2)读取策略:如果cache命中,则直接从cache中读取,不再读取对应的内存区域,如果cache未命中,则开辟对应的cache区域,并从对应的内存区域中将数据同步至cache,并读取cache。
2.Write back, write and read allocate
(1)写入策略:如果cache命中,则只写入cache,不再写入到对应的内存区域;如果未命中,则开辟对应的cache,并同时向cache和对应的内存区域写入。
(2)读取策略:如果cache命中,则直接从cache中读取,不再读取对应的内存区域,如果cache未命中,则开辟对应的cache区域,并从对应的内存区域中将数据同步至cache,并读取cache。
3.Write back, no write allocate
(1)写入策略:如果cache命中,则只写入cache,不再写入到对应的内存区域;如果未命中,则只写入对应的内存区域,不开辟新的cache。
(2)读取策略:如果cache命中,则直接从cache中读取,不再读取对应的内存区域,如果cache未命中,则开辟对应的cache区域,并从对应的内存区域中将数据同步至cache,并读取cache。
通过上面介绍的特性,可以看到,使用的时候最好使用Write through, no write allocate这种模式,因为cache命中,则同时向cache和对应的内存区域中写入,不用担心数据的不统一。H7系列使用cache代码的执行速度提高两倍以上,当然这个两倍只是我大概试了一下。
速度 | 地址 | 总线 | 大小 | |
ITCM | 400MHz | 0x0000 0000 | 64bit | 64KB |
DTCM | 400MHz | 0x2000 0000 | 64bit | 128KB |
速度 | 地址 | 总线 | 大小 | |
AXI SRAM | 200MHz | 0x2400 0000 | 64bit | 512KB |
速度 | 地址 | 总线 | 大小 | |
SRAM1 | 200MHz | 0x3000 0000 | 32bit | 128KB |
SRAM2 | 200MHz | 0x3002 0000 | 32bit | 128KB |
SRAM3 | 200MHz | 0x3004 0000 | 32bit | 32KB |
速度 | 地址 | 总线 | 大小 | |
SRAM4 | 200MHz | 0x3800 0000 | 32bit | 64KB |
速度 | 地址 | 总线 | 大小 | |
Backup SRAM | 200MHz | 0x3880 0000 | 32bit | 4KB |
结合这张图,可以看出ITCM、DTCM、AXI SRAM是不可以使用DMA的。
如果使用了可以使用DMA的SRAM,需要注意,如果开启了CACHE,记得要配置MPU,否则DMA数据很有可能是不更新的。再操作flash的时候,也要注意清除D-CACHE,否则写入会有问题。