Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

TL;DR: documentation states I have to enable a specific memory region in the microcontroller before I can use it. However, I can use it before enabling it, or even after disabling it. How is this possible?


I'm currently developing an application for the STM32H743 microcontroller. I don't understand how the RAM seems to work correctly while the clock is disabled.

This MCU has multiple memories, spread over multiple power domains:

  • In D1 domain it has ITCMRAM + DTCMRAM + AXI SRAM (64 + 128 + 512 kB)
  • In D2 domain it has SRAM1 + SRAM2 + SRAM3 (128 + 128 + 32 kB)
  • In D3 domain it has SRAM4 + Backup SRAM (64 + 4 kB)

I want to use the SRAM1. In the reference manual (RM0433 Rev. 7) it is stated at page 366 that:

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

In the register settings at page 452 it is described how to do this:

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.

So, the default value (after reset) is 0, which means the SRAM1 interface is disabled.

In this thread on the STM Community forum the question was why the D2 RAM wasn't working correctly and the solution was to enable the D2 RAM clocks. The "correct" way to do this is in SystemInit() (part of the STM32H7 HAL). In system_stm32h7xx.c we can find the following code parts:

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

So, to use D2 SRAM, the macro DATA_IN_D2_SRAM should be defined (or you must manually enable the clock using __HAL_RCC_D2SRAM1_CLK_ENABLE()).

However, I don't have this macro defined, and even when I manually disable the clocks, the RAM seems to be working perfectly fine.

My main task (I'm running FreeRTOS, and this is the only task right now) is like this:

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 (;;) {}
}

The memory test completely fills the D2 SRAM with known data, then calculates a CRC over it. The CRC is correct. I already have verified that the buffer is really placed in the D2 SRAM (memory address 0x30000400 is within the range 0x30000000-0x3001FFFF of SRAM1). The value of RCC->AHB2ENR is confirmed to be 0 (all clocks disabled). I also confirmed that the address of RCC->AHB2ENR is 0x580244DC, as stated in the datasheet.

The data cache is disabled.

What am I missing here? Why is this memory readable and writable when the clocks are disabled?


UPDATE: On request, here is the code of my memory test, from which I conclude that the memory can be written and read successfully:

// 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'))
}
like image 374
wovano Avatar asked Nov 19 '20 09:11

wovano


1 Answers

After lots of testing and investigating I found out that the D2 SRAM was disabled (as documented and expected) in a minimal application using the SysTick and only a few LEDs to make the test results visible. However, when using a timer (TIM1) instead of SysTick, or when enabling a USART, the D2 SRAM was enabled as well, even when I did not enable it in my code. In fact, adding either one of the following lines of code would implicitly enable the D2 SRAM:

__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_USART3_CLK_ENABLE();

STM support has confirmed this behavior:

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.

I'm still looking for a reliable source (reference manual) where this behavior is documented, but it seems to be a plausible explanation.

In practice I think this means that the D2 SRAM will almost always be enabled automagically so you don't have to care about it, at least for the most common use cases (e.g. when using any peripheral or the DMA controllers). Only when you want to use the D2 SRAM but none of the D2 peripherals, you would have to manually enable the SRAM clocks. This would also be the case for the startup code, where (if you choose to implement this) the D2 SRAM will be initialized before any of the peripherals are enabled.

like image 149
wovano Avatar answered Oct 02 '22 23:10

wovano