TL;DR:文档规定,在使用微控制器之前,我必须启用微控制器中的特定内存区域。但是,我可以在启用它之前使用它,甚至可以在禁用它之后使用它。这怎么可能?
我目前正在开发STM32H743微控制器的应用程序。我不明白当时钟被禁用时,RAM是如何正常工作的。
该MCU具有多个存储器,分布在多个电源域上:
- 在D1域中,它具有ITCMRAM+DTCMRAM+AXI SRAM(64+128+512 kB(
- 在D2域中,它具有SRAM1+SRAM2+SRAM3(128+128+32kB(
- 在D3域中,它具有SRAM4+备份SRAM(64+4 kB(
我想使用SRAM1。参考手册(RM0433 Rev.7(第366页规定:
如果CPU想要使用位于D2域(SRAM1、SRAM2和SRAM3(中的存储器,则必须启用它们。
在第452页的寄存器设置中,描述了如何做到这一点:
RCC AHB2时钟寄存器(RCC_AHB2ENR(:
SRAM1EN:SRAM1块启用
通过软件设置和重置。当设置时,该位表示SRAM1是由CPU分配的。它导致D2域还考虑CPU操作模式,即当CPU在CRun中
0:SRAM1接口时钟被禁用。(重置后默认(
1:SRAM1接口时钟启用
因此,默认值(重置后(为0,这意味着SRAM1接口被禁用。
在STM社区论坛的这个帖子中,问题是为什么D2 RAM不能正常工作,解决方案是启用D2 RAM时钟。";正确的";实现这一点的方法是在SystemInit()
(STM32H7HAL的一部分(中。在system_stm32h7xx.c中,我们可以找到以下代码部分:
/************************* Miscellaneous Configuration ************************/
/*!< Uncomment the following line if you need to use initialized data in D2 domain SRAM (AHB SRAM)
*/
// #define DATA_IN_D2_SRAM
(...)
void SystemInit(void)
{
(...)
#if defined(DATA_IN_D2_SRAM)
/* in case of initialized data in D2 SRAM (AHB SRAM) , enable the D2 SRAM clock (AHB SRAM clock)
*/
# if defined(RCC_AHB2ENR_D2SRAM3EN)
RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN | RCC_AHB2ENR_D2SRAM3EN);
# elif defined(RCC_AHB2ENR_D2SRAM2EN)
RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN);
# else
RCC->AHB2ENR |= (RCC_AHB2ENR_AHBSRAM1EN | RCC_AHB2ENR_AHBSRAM2EN);
# endif /* RCC_AHB2ENR_D2SRAM3EN */
tmpreg = RCC->AHB2ENR;
(void)tmpreg;
#endif /* DATA_IN_D2_SRAM */
(...)
}
因此,要使用D2 SRAM,应该定义宏DATA_IN_D2_SRAM
(或者必须使用__HAL_RCC_D2SRAM1_CLK_ENABLE()
手动启用时钟(。
但是,我没有定义这个宏,即使我手动禁用时钟,RAM似乎也能正常工作。
我的主要任务(我正在运行FreeRTOS,这是目前唯一的任务(是这样的:
void main_task(void * argument)
{
__HAL_RCC_D2SRAM1_CLK_DISABLE();
__HAL_RCC_D2SRAM2_CLK_DISABLE();
__HAL_RCC_D2SRAM3_CLK_DISABLE();
mem_test(); // expected to fail, but runs successfully
for (;;) {}
}
内存测试用已知数据完全填充D2 SRAM,然后在其上计算CRC。CRC是正确的。我已经验证了缓冲区是否真的放置在D2 SRAM中(内存地址0x300000040在SRAM1的0x300000-0x3001FFFF范围内(。RCC->AHB2ENR
的值被确认为0(所有时钟被禁用(。我还确认了RCC->AHB2ENR
的地址是0x580244DC,如数据表中所述。
数据缓存已禁用。
我在这里错过了什么?为什么当时钟被禁用时,这个内存是可读写的?
更新:根据请求,以下是我的内存测试代码,我从中得出结论,内存可以成功写入和读取:
// NB: The sections are defined in the linker script.
static char test_data_d1[16] __attribute__((section(".RAM_D1_data"))) = "Test data in D1";
static char test_data_d2[16] __attribute__((section(".RAM_D2_data"))) = "Test data in D2";
static char test_data_d3[16] __attribute__((section(".RAM_D3_data"))) = "Test data in D3";
static char buffer_d1[256 * 1024ul] __attribute__((section(".RAM_D1_bss")));
static char buffer_d2[256 * 1024ul] __attribute__((section(".RAM_D2_bss")));
static char buffer_d3[ 32 * 1024ul] __attribute__((section(".RAM_D3_bss")));
static void mem_test(void)
{
// Fill the buffers each with a different test pattern.
fill_buffer_with_test_data(buffer_d1, sizeof(buffer_d1), test_data_d1);
fill_buffer_with_test_data(buffer_d2, sizeof(buffer_d2), test_data_d2);
fill_buffer_with_test_data(buffer_d3, sizeof(buffer_d3), test_data_d3);
uint32_t crc_d1 = crc32b((uint8_t const *)buffer_d1, sizeof(buffer_d1));
uint32_t crc_d2 = crc32b((uint8_t const *)buffer_d2, sizeof(buffer_d2));
uint32_t crc_d3 = crc32b((uint8_t const *)buffer_d3, sizeof(buffer_d3));
printf("CRC buffer_d1 = 0x%08lXn", crc_d1);
printf("CRC buffer_d2 = 0x%08lXn", crc_d2);
printf("CRC buffer_d3 = 0x%08lXn", crc_d3);
assert(0xC29DFAED == crc_d1); // Python: hex(binascii.crc32(16384 * b'Test data in D1 '))
assert(0x73B70C2A == crc_d2); // Python: hex(binascii.crc32(16384 * b'Test data in D2 '))
assert(0xC30AE71E == crc_d3); // Python: hex(binascii.crc32(2048 * b'Test data in D3 '))
}
经过大量的测试和调查,我发现D2 SRAM在使用SysTick的最小应用程序中被禁用(如文档所示和预期的(,并且只有几个LED使测试结果可见。然而,当使用计时器(TIM1(而不是SysTick时,或者当启用USART时,D2 SRAM也被启用,即使我在代码中没有启用它。事实上,添加以下任何一行代码都会隐含地启用D2 SRAM:
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_USART3_CLK_ENABLE();
STM支持已经证实了这种行为:
D2中的任何外围设备一被激活,D2 SRAM就被激活。这意味着,如果您为位于D2域中的任何外围设备(AHB1、AHB2、APB1和APB2(启用时钟,则即使
RCC->AHB2ENR
为0,D2 SRAM也处于活动状态。
我仍在寻找一个可靠的来源(参考手册(来记录这种行为,但这似乎是一个合理的解释。
在实践中,我认为这意味着D2 SRAM几乎总是自动启用,所以你不必关心它,至少在最常见的使用情况下(例如,当使用任何外围设备或DMA控制器时(。只有当您想使用D2 SRAM而不想使用D2外围设备时,您才必须手动启用SRAM时钟。启动代码也是如此,其中(如果您选择实现此功能(D2 SRAM将在启用任何外围设备之前初始化。