本帖最后由 tpgf 于 2024-10-29 08:23 编辑
STM32H7 内部SRAM空间的配置使用
STM32H750xB器件集成了高速嵌入式存储器,配有128 KB的Flash内存,高达1 MB的RAM(包括192 KB的TCM RAM、高达864 KB的用户SRAM和4 KB的备份SRAM),以及大量连接到APB总线、AHB总线、2个32位多AHB总线矩阵的增强型I/O和外设,支持内部及外部存储器访问的多层AXI互连。
内部 SRAM
STM32H7x3 器件具有:
多达 864 KB 的系统 SRAM
128 KB 的数据 TCM RAM
64 KB 的指令 TCM RAM
4 K 字节的备份 SRAM
嵌入式系统 SRAM 最多可分为五个块:
AXI SRAM(D1 域):
– 映射到地址 0x2400 0000 的 AXI SRAM,可供除 BDMA 外的所有系统主设备通过
D1 域 AXI 总线矩阵访问
AHB SRAM(D2 域):
– 映射到地址 0x3000 0000 的 AHB SRAM1,可供除 BDMA 外的所有系统主设备通
过 D2 域 AHB 矩阵访问
– 映射到地址 0x3002 0000 的 AHB SRAM2,可供除 BDMA 外的所有系统主设备通
过 D2 域 AHB 矩阵访问
– 映射到地址 0x3004 0000 的 AHB SRAM3,可供除 BDMA 外的所有系统主设备通
过 D2 域 AHB 矩阵访问
AHB SRAM(D3 域):
– 映射到地址 0x3800 0000 的 AHB SRAM4,可供大多数系统主设备通过 D3 域 AHB 矩阵访问系统 AHB SRAM 可按字节、半字(16 位单元)或全字(32 位单元)访问,而系统 AXI SRAM 可按字节、半字、全字或双字(64 位单位)访问。这些存储器可在最高系统时钟频率下以 0 等待周期寻址。
当 AHB 主设备对 SRAM 中的一部分进行读/写访问的同时,以太网 MAC 或 USB OTG HS 外设可以访问 SRAM 的另一部分。例如,以太网 MAC 访问 SRAM2,同时 CPU 访SRAM1。
TCM SRAM 专用于 Cortex®-M7:
TCM 接口上的 DTCM-RAM 映射到地址 0x2000 0000,可供 Cortex®-M7 访问,并且可供 MDMA 通过 Cortex®-M7 CPU 的 AHBS 从总线访问。
TCM 接口上的 ITCM-RAM 映射到地址 0x0000 0000,可供 Cortex®-M7 访问,并且可供 MDMA 通过 Cortex®-M7 CPU 的 AHBS 从总线访问。
备份 RAM 映射到地址 0x3880 0000,可供大多数系统主设备通过 D3 域的 AHB 矩阵访问。
配置使用
用户变量定义到指定的CCM 或者SDRAM中:
//AC5 Compliler
__align(4) uint8_t SendBuff[SEND_BUF_SIZE]__attribute((at(0X24040000)));
//__ARMCC_VERSION >= 6010050
__attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];
自定义分散加载(.SCT)文件
片内启动,片上RAM配置(Start addr:0x08000000)
LR_IROM1 0x08000000 0x00200000 { ; load region size_region
ER_IROM1 0x08000000 0x00200000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
; RW data - 128KB DTCM
RW_IRAM1 0x20000000 0x00020000 {
.ANY (+RW +ZI)
}
; RW data - 512KB AXI SRAM
RW_IRAM2 0x24000000 0x00080000 {
*(.RAM_D1)
}
; RW data - 128KB SRAM1(0x30000000) + 128KB SRAM2(0x3002 0000) + 32KB SRAM3(0x30040000)
RW_IRAM3 0x30000000 0x00048000 {
*(.RAM_D2)
}
; RW data - 64KB SRAM4(0x38000000)
RW_IRAM4 0x38000000 0x00010000 {
*(.RAM_D3)
}
片外启动,片上RAM配置(Start addr:0x90000000)
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x90000000 0x20000000 { ; load region size_region
ER_IROM1 0x90000000 0x20000000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00020000 { ; RW data - 128KB DTCM
.ANY (+RW +ZI)
}
RW_IRAM2 0x24000000 0x00080000 { ; RW data - 512KB AXI SRAM
.ANY (+RW +ZI)
}
; RW data - 128KB SRAM1(0x30000000) + 128KB SRAM2(0x3002 0000) + 32KB SRAM3(0x30040000)
RW_IRAM3 0x30000000 0x00048000 {
*(.RAM_D2)
}
; RW data - 64KB SRAM4(0x38000000)
RW_IRAM4 0x38000000 0x00010000 {
*(.RAM_D3)
}
}
以下内容参考来自:安富莱stm32-V7用户手册
LR_IROM1 是 Load Region 加载域,ER_IROM1 是 Execution Region 执行域。首地址都是 0x0800 0000,大小都是 0x0020 0000,即 STM32H7 的 Flash 地址和对应大小。
加载域就是程序在 Flash 中的实际存储,而运行域是芯片上电后的运行状态,通过下面的框图可以有一个感性的认识:
通过上面的框图可以看出,RW 区也是要存储到 ROM/Flash 里面的,在执行映像之前,必须将已初始化的 RW 数据从 ROM 中复制到 RAM 中的执行地址并创建 ZI Section(初始化为 0 的变量区)。
的*.o (RESET, +First):在启动文件 startup_stm32h750xx.s 有个段名为 RESET 的代码段,主要存储了中断向量表。这里是将其存放在 Flash 的首地址。
*(InRoot$$Sections):将 MDK 的一些库文件全部放在根域,比如__main.o, _scatter*.o, _dc*.o
.ANY (+RO):将目标文件中所有具有 RO 只读属性的数据放在这里,即 ER_IROM1。
RW_IRAM1 是执行域,配置的是 DTCM,首地址 0x2000 0000,大小 128KB。将目标文件中所有具有 RW 和 ZI 数据放在这里。
RW_IRAM2 是执行域,配置的是 AXI SRAM,首地址 0x24000000,大小 512KB。给这个域专门配了一个名字 .RAM_D1。这样就可以通过__attribute__((section(“name”)))将其分配到这个 RAM 域。
RW_IRAM3 是执行域,配置的是 D2 域的 SRAM1,SRAM2 和 SRAM3,首地址0x30000000,共计大小 288KB。给这个域专门配了一个名字 .RAM_D2。这样就可以通过__attribute__((section(“name”)))将其分配到这个 RAM 域。
RW_IRAM3 是执行域,配置的是 D3 域的 SRAM4,首地址 0x38000000,共计大小
64KB。给这个域专门配了一个名字 .RAM_D3。这样就可以通过__attribute__((section(“name”))),将其分配到这个 RAM 域。
在外设开启DMA传输模式下同时启用了I缓存和D缓存下,有必要开启这样的设置,不然读取的数据容易出错。
I缓存和D缓存启用下,接收数组变量没有指定情况下,读取数据:
I缓存和D缓存启用下,接收数组变量指定SRAM情况下,读取数据可以不用在读前读后,分别作启用和禁用的操作。
相关函数
void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize):D-Cache Clean by address
作用: 用于清除指定地址范围内的数据缓存。它将缓存中的数据写回到主存储器中,以确保缓存中的数据与主存储器中的数据一致。
使用场景: 当你需要确保某个特定内存区域的数据已经被更新到主存储器时,可以使用这个函数。例如,在将数据写入外设之前,或者在修改了关键数据之后。
void SCB_CleanInvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize):D-Cache Clean and Invalidate by address
作用: 这个函数不仅会清除指定地址范围内的数据缓存,还会使这些缓存行无效。这意味着在调用这个函数后,处理器将不会从缓存中读取这些地址的数据,而是直接从主存储器中读取。
使用场景: 当你需要确保某个内存区域的数据是最新的,并且不希望处理器从缓存中读取旧的数据时,可以使用这个函数。例如,在加载新的程序代码或数据到内存中之后。
SCB_CleanDCache()函数:
作用: 这个函数用于将数据缓存中的数据写回到主存储器中,以确保缓存中的数据与主存储器中的数据一致。这通常在以下情况下使用:
在将数据写入外设之前,确保数据已经更新到主存储器。
在修改了关键数据之后,确保这些数据已经被正确地写回到主存储器。
特点: 只执行写回操作,不使缓存行无效。这意味着处理器仍然可以从缓存中读取数据,即使这些数据已经被写回到主存储器。
SCB_CleanInvalidateDCache() 函数:
作用: 这个函数不仅会将数据缓存中的数据写回到主存储器,还会使这些缓存行无效。这意味着在调用这个函数后,处理器将不会从缓存中读取这些地址的数据,而是直接从主存储器中读取。这通常在以下情况下使用:
在加载新的程序代码或数据到内存中之后,确保处理器使用的是最新的数据。
在需要确保数据一致性的关键操作之前,例如系统启动或上下文切换。
特点: 执行写回操作并且使缓存行无效。这确保了处理器在需要时总是从主存储器中获取最新的数据。
MPU内存保护单元
/**
* @brief Configure the MPU attributes
* @param None
* @retval None
*/
static void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable the MPU */
HAL_MPU_Disable();
/* Configure the MPU as Strongly ordered for not defined regions */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x00;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Configure the MPU attributes as Normal Non Cacheable for SRAM1 */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
片外启动MPU初始化配置:
void MPU_RegionConfig(void)
{
/* Disable MPU */
HAL_MPU_Disable();
/* 设置QSPI FLASH空间的MPU保护 */
SCB->SHCSR &= ~(1 << 16); /* 禁止MemManage */
MPU->CTRL &= ~(1 << 0); /* 禁止MPU */
MPU->RNR = 0; /* 设置保护区域编号为0(1~7可以给其他内存用) */
MPU->RBAR = 0X90000000; /* 基地址为0X9000 000, 即QSPI的起始地址 */
MPU->RASR = 0X0303002D; /* 设置相关保护参数(禁止共用, 允许cache, 允许缓冲), 详见MPU实验的解析 */
MPU->CTRL = (1 << 2) | (1 << 0);/* 使能PRIVDEFENA, 使能MPU */
SCB->SHCSR |= 1 << 16; /* 使能MemManage */
/* Enable MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
————————————————
本文为Perseverance52博主原创文章,未经博主允许,不得转载!
原文链接:https://blog.csdn.net/weixin_42880082/article/details/143302092
|