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

Why does D2 RAM work correctly even when clock is disabled?

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


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

这个 MCU 有多个内存,分布在多个电源域:

我想使用SRAM1。在参考手册(RM0433 修订版 7)中,第 366 页指出:

If the CPU wants to use memories located into D2 domain (SRAM1, SRAM2 and SRAM3), it has to enable them.

在第 452 页的寄存器设置中描述了如何执行此操作:

RCC AHB2 Clock Register (RCC_AHB2ENR):

SRAM1EN: SRAM1 block enable
Set and reset by software. When set, this bit indicates that the SRAM1 is allocated by the CPU. It causes the D2 domain to take into account also the CPU operation modes, i.e. keeping D2 domain in DRun when the CPU is in CRun.
0: SRAM1 interface clock is disabled. (default after reset)
1: SRAM1 interface clock is enabled.

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

在 STM 社区论坛的 this thread 中,问题是为什么 D2 RAM 无法正常工作,解决方案是启用 D2 RAM 时钟。执行此操作的“正确”方法是 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 */
    (...)
}

因此,要使用 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中(内存地址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[=12=]'))
    assert(0x73B70C2A == crc_d2); // Python: hex(binascii.crc32(16384 * b'Test data in D2[=12=]'))
    assert(0xC30AE71E == crc_d3); // Python: hex(binascii.crc32(2048 * b'Test data in D3[=12=]'))
}

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

__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_USART3_CLK_ENABLE();

STM 支持已确认此行为:

D2 SRAM is activated as soon as any peripheral in D2 is activated. It means that If you enable clock for any peripheral located in D2 domain (AHB1, AHB2, APB1 and APB2), D2 SRAM is active even if RCC->AHB2ENR is 0.

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

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