6

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 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 */
    (...)
}

DATA_IN_D2_SRAM因此,要使用 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\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'))
}
4

1 回答 1

6

经过大量测试和调查后,我发现 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。

于 2020-12-07T10:28:41.893 回答