Почему D2 RAM работает корректно даже при отключенных часах?

TL;DR: в документации указано, что я должен включить определенную область памяти в микроконтроллере, прежде чем я смогу ее использовать. Однако я могу использовать его до включения или даже после отключения. Как это возможно?


Сейчас я разрабатываю приложение для микроконтроллера STM32H743. Я не понимаю, как оперативная память работает правильно при отключенных часах.

Этот MCU имеет несколько запоминающих устройств, распределенных по нескольким доменам мощности:

  • В домене D1 это ITCMRAM + DTCMRAM + AXI SRAM (64 + 128 + 512 кБ)
  • В домене D2 он имеет SRAM1 + SRAM2 + SRAM3 (128 + 128 + 32 кБ)
  • В домене D3 есть SRAM4 + Backup SRAM (64 + 4 кБ)

Я хочу использовать SRAM1. В справочном руководстве ( RM0433 Ред. 7) на странице 366 указано, что:

Если ЦП хочет использовать память, расположенную в домене D2 (SRAM1, SRAM2 и SRAM3), он должен включить их.

В настройках реестра на странице 452 описано, как это сделать:

Регистр часов RCC AHB2 (RCC_AHB2ENR):

SRAM1EN: включение блока SRAM1
Устанавливается и сбрасывается программно. Когда установлен, этот бит указывает, что SRAM1 выделена ЦП. Это заставляет домен D2 учитывать также режимы работы ЦП, т. Е. Сохранение домена D2 в режиме DRun, когда ЦП находится в состоянии CRun.
0: синхронизация интерфейса SRAM1 отключена. (по умолчанию после сброса)
1: синхронизация интерфейса SRAM1 включена.

Итак, значение по умолчанию (после сброса) - 0, что означает, что интерфейс SRAM1 отключен.

В этой ветке на форуме сообщества STM вопрос заключался в том, почему ОЗУ D2 не работает должным образом, и решением было включить часы ОЗУ 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 */
    (...)
}

Итак, чтобы использовать D2 SRAM, макрос DATA_IN_D2_SRAM должны быть определены (или вы должны вручную включить часы, используя __HAL_RCC_D2SRAM1_CLK_ENABLE()).

Однако у меня нет этого макроса, и даже когда я вручную отключаю часы, ОЗУ, кажется, работает отлично.

Моя основная задача (я запускаю 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 находится в диапазоне 0x30000000-0x3001FFFF SRAM1). Значение 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'))
}

1 ответ

После большого количества тестов и исследований я обнаружил, что D2 SRAM была отключена (как задокументировано и ожидалось) в минимальном приложении, использующем SysTick и только несколько светодиодов, чтобы сделать результаты теста видимыми. Однако при использовании таймера (TIM1) вместо SysTick или при включении USART также включалась D2 SRAM, даже если я не включал ее в своем коде. На самом деле, добавление любой из следующих строк кода неявно активирует D2 SRAM:

      __HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_USART3_CLK_ENABLE();

Поддержка STM подтвердила это поведение:

D2 SRAM активируется, как только активируется любое периферийное устройство в D2. Это означает, что если вы включите часы для любого периферийного устройства, расположенного в домене D2 (AHB1, AHB2, APB1 и APB2), D2 SRAM активна, даже если RCC->AHB2ENRравно 0.

Я все еще ищу надежный источник (справочное руководство), где это поведение задокументировано, но это кажется правдоподобным объяснением.

На практике я думаю, что это означает, что D2 SRAM почти всегда будет включаться автоматически , поэтому вам не нужно заботиться об этом, по крайней мере, в наиболее распространенных случаях использования (например, при использовании любого периферийного устройства или контроллеров DMA). Только когда вы хотите использовать D2 SRAM, но не хотите использовать периферийные устройства D2, вам придется вручную включить часы SRAM. Это также относится к коду запуска, где (если вы решите реализовать это) D2 SRAM будет инициализирована до включения любого из периферийных устройств.

Другие вопросы по тегам