为什么即使禁用时钟 D2 RAM 也能正常工作?

wov*_*ano 6 microcontroller clock stm32 stm32-hal stm32h7

TL;DR:文档说明我必须先在微控制器中启用特定的内存区域,然后才能使用它。但是,我可以在启用它之前使用它,甚至可以在禁用它之后使用它。这怎么可能?


我目前正在为 STM32H743 微控制器开发一个应用程序。我不明白当时钟被禁用时 RAM 似乎如何正常工作。

该 MCU 具有多个存储器,分布在多个电源域中:

  • 在 D1 域中,它具有 ITCMRAM + DTCMRAM + AXI SRAM (64 + 128 + 512 kB)
  • 在 D2 域中,它有 SRAM1 + SRAM2 + SRAM3 (128 + 128 + 32 kB)
  • 在 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 时将 D2 域保持在 DRun 中。
0:SRAM1 接口时钟禁用。(复位后默认)
1:SRAM1 接口时钟使能。

因此,默认值(复位后)为 0,这意味着 SRAM1 接口被禁用。

此线程上的STM社区论坛的问题是,为什么D2 RAM未正常工作和解决方案是使D2内存时钟。执行此操作的“正确”方法是SystemInit()(STM32H7 HAL 的一部分)。在 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 */
    (...)
}
Run Code Online (Sandbox Code Playgroud)

因此,要使用 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 (;;) {}
}
Run Code Online (Sandbox Code Playgroud)

内存测试用已知数据完全填充 D2 SRAM,然后计算 CRC。CRC 是正确的。我已经验证过缓冲区确实放在D2 SRAM中(内存地址0x30000400在SRAM1的0x30000000-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%08lX\n", crc_d1);
    printf("CRC buffer_d2 = 0x%08lX\n", crc_d2);
    printf("CRC buffer_d3 = 0x%08lX\n", crc_d3);

    assert(0xC29DFAED == crc_d1); // Python: hex(binascii.crc32(16384 * b'Test data in D1\0'))
    assert(0x73B70C2A == crc_d2); // Python: hex(binascii.crc32(16384 * b'Test data in D2\0'))
    assert(0xC30AE71E == crc_d3); // Python: hex(binascii.crc32(2048 * b'Test data in D3\0'))
}
Run Code Online (Sandbox Code Playgroud)

wov*_*ano 5

经过大量测试和调查,我发现 D2 SRAM 在使用 SysTick 的最小应用程序中被禁用(如记录和预期的那样),并且只有几个 LED 使测试结果可见。但是,当使用定时器 (TIM1) 代替 SysTick 或启用 USART 时,D2 SRAM 也会启用,即使我没有在我的代码中启用它。事实上,添加以下任一行代码都会隐式启用 D2 SRAM:

__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_USART3_CLK_ENABLE();
Run Code Online (Sandbox Code Playgroud)

STM 支持已确认此行为:

一旦 D2 中的任何外设被激活,D2 SRAM 就会被激活。这意味着如果您为位于 D2 域(AHB1、AHB2、APB1 和 APB2)中的任何外设启用时钟,即使 D2 SRAMRCC->AHB2ENR为 0 ,也处于活动状态。

我仍在寻找记录此行为的可靠来源(参考手册),但这似乎是一个合理的解释。

在实践中,我认为这意味着 D2 SRAM 几乎总是自动启用,因此您不必关心它,至少对于最常见的用例(例如,当使用任何外设或 DMA 控制器时)。只有当您想使用 D2 SRAM 而不想使用 D2 外设时,您才必须手动启用 SRAM 时钟。这也适用于启动代码,其中(如果您选择实现此功能)将在启用任何外设之前初始化 D2 SRAM 。