Skip to main content
Visitor II
September 30, 2021
Question

4GB write, read and erase EMMC with 4-bit DDR on STM32H7B0 using STM32Cube FW_H7 V1.9.0

  • September 30, 2021
  • 5 replies
  • 3985 views

Hi,

I am trying to write, read and erase properly on a 4 GB Insignis EMMC (NSEC53K004-IT/-AT) with up to 250 MB/s read and 14 MB/s write.

I currently use the STM32Cube FW_H7 V1.9.0 and am unsure regarding the adjustments that need to be done to the generated code, as there have been fixes to described issues that I found here in the forums in other threads.

e.g. https://community.st.com/s/question/0D50X00009sVjaySAC/stm32h753-sdmmc-driver-not-working-properly-sdk-v130

(where a DTMODE adjustment is described for DDR)

Would that still be necessary? The mode 3 still is not officially defined.

// in stm32h753xx.h
 #define SDMMC_DCTRL_DTMODE_2 (0x3U << SDMMC_DCTRL_DTMODE_Pos) 
 
 //in stm32h7xx_ll_sdmmc.h
 #define SDMMC_TRANSFER_MODE_BLOCK_STOP		 SDMMC_DCTRL_DTMODE_2
 
 // in stm32h7xx_hal_mmc.c
 // in read/write dma function set transfermode to:
 config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK_STOP;

I am also unaware what is the current situation with this remark from the thread, as I don't see the call in with the current V1.9.0

Same as you, I noticed that the cmd16(SDMMC_CmdBlockLength()) in the write/read function must be put in an IF statement to be ignored if we are in DDR mode.

The 4 data pins in Cube IDE are configured for alternative push-pull and the global interrupt is enabled as well as hardware flow control. Rising transition and not power save for the CLK.

The only adjustment I made was this one in the MCC_InitCard() function, as the HAL_MMC_Init() call from the main now includes the HAL_MMC_ConfigWideBusOperation() call and it would not configure for DDR without these set bit calls.

/* Configure the SDMMC peripheral */
 Init = hmmc->Init;
 Init.BusWide = SDMMC_BUS_WIDE_1B;
 (void)SDMMC_Init(hmmc->Instance, Init);
 SET_BIT((hmmc)->Instance->CLKCR, SDMMC_CLKCR_DDR);
 SET_BIT((hmmc)->Instance->CLKCR, SDMMC_CLKCR_BUSSPEED);
 /* All cards are initialized */
 return HAL_MMC_ERROR_NONE;

Writing seems to work, but is not faster than using 1-bit and default speed, I am very far away from 14 MB/s and therefore assume that DDR is not working at all. The CLK is set to 50 MHz, but I also tried 25 MHz using the EMMC CLK divider or setting it to 0.

Reading does not work, as even with single block reading, I get a busy response from the DMA Read function. My test code is as follows:

Interations refer to the block intertions to be written. I tested for 1 block writes and 8 block writes (4kB). MX_SDMMC2_MMC_Init() has been called.

for (int iteration = 0; iteration < iterations; iteration++) {
 errorState = HAL_MMC_WriteBlocks_DMA(&hmmc2, wData, blockCount, blocksPerWrite);
 while (HAL_MMC_GetCardState(&hmmc2) != HAL_MMC_CARD_TRANSFER) {
 HAL_Delay(1);
 }
 if(errorState != 0){
 HAL_Delay(50);
 }
 errorState = HAL_MMC_ReadBlocks_DMA(&hmmc2, rData, blockCount, blocksPerWrite);
 while (HAL_MMC_GetCardState(&hmmc2) != HAL_MMC_CARD_TRANSFER) {
 HAL_Delay(1);
 }
 if (memcmp(wData, rData, bytesPerWrite) == 0) {
 	 status = 0;
 }
 else {
 status = 1;
 }
 blockCount += blocksPerWrite;
 }

I reckon that I need to implement HAL_MMC_RxCpltCallback to avoid timeout, but would this help with the DMA being busy?

Can somebody tell me what might be the issue here and which adjustments would be necessary to get the DDR up and running using DMA or even without it?

Best regards

    This topic has been closed for replies.

    5 replies

    Super User
    September 30, 2021

    Regarding the last block of code you wrote:

    HAL_MMC_GetCardState sends a command, which it can't do if the operation is ongoing. There doesn't appear to be a mechanism in HAL preventing this, at least in the version of the library I'm checking.

    You should instead monitor the state of hmmc2.State and wait until it's HAL_MMC_STATE_READY, then call HAL_MMC_GetCardState to check for errors.

    The HAL_Delay calls here are not necessary and will only slow down the operation. (But you should only be calling HAL_MMC_GetCardState once anyway.)

    indesaf43Author
    Visitor II
    September 30, 2021

    Hi TDK, thank your support! Do know of a best practice example for using these Write and Read functions in combination with EMMC?

    Super User
    September 30, 2021
    Best practice? Not really, other than understanding how HAL expects you to call functions. It is laid out relatively well at the top of the relevant source file.
    Developing eMMC functionality is complicated, especially at higher data speeds. I would be extremely surprised if HAL could do this out of the box. It requires a low-level understanding of what is going on at the card level, and if you're using HAL, how those functions translate into MMC commands. For higher data rates, it requires an understanding of how to minimize the time spent not transferring data while still remaining responsive to user input, if applicable.
    All of those add up to requiring a relatively specialized skillset and time investment.
    indesaf43Author
    Visitor II
    September 30, 2021

    Without the GetCardState call, I get an error for the Write.

    Graduate II
    September 30, 2021

    To get any kind of speed you're going to have to write dozens of sectors at once, the erase block size is probably of the order of 128KB, and it's going to be juggling those behind the scenes. You'd likely need to write a stream of 32MB to get any appreciation of the sustained performance.

    Not going to get anywhere need 250 MB/s on this platform.

    Custom board presumably? Do you have a debug UART? Which UART, which pins?

    indesaf43Author
    Visitor II
    September 30, 2021

    Yes, it's a custom board, it's just about proof of concept and that everything is connected correctly. Also, a bit of a load and function test. Those speeds are not required, but it should be faster than the extrapolated 2h.

    I have several UARTS as option, one is specifically thought for log output and only offers one pin as Tx for the STM32H7, PB6,14,15 and PD 0/1 would be options - no flow control available. Can you tell me how I can confirm that it actually uses the configured 4 lines?

    Graduate II
    September 30, 2021

    >>Can you tell me how I can confirm that it actually uses the configured 4 lines?

    I typically unpack the SDMMC and RCC/PLL clock registers to report the actual settings the HW is using.

    Do you have USART1 wired to a terminal currently, or are you using the SWO pin from the SWD interface for diagnostic output?

    indesaf43Author
    Visitor II
    October 1, 2021

    When calling HAL_MMC_ConfigSpeedBusOperation with SDMMC_SPEED_MODE_DDR, it wants to set the device to HS mode and calls another MMC_PwrClassUpdate() after the MMC_PwrClassUpdate call that is in the implicitly called in HAL_MMC_ConfigWideBusOperation(). However, reading the POWER_CLASS register in this call, it hangs up in the posted polling. Can somebody tell me why?

    I see that HAL_MMC_ConfigWideBusOperation() sets the bus width to 0x01, whereas only in the HAL_MMC_ConfigSpeedBusOperation() it is set to 0x05 as referenced in the JEDEC 5.0 standard. Also, it only does so if I set the bits as described earlier. As those bits supposed to be set in the HAL_MMC_ConfigSpeedBusOperation() I removed them again, but it does not succeed at the mentioned POWER_CLASS register read, which has worked in the first call.

    Explorer
    January 22, 2025

    I see the same happening on STM32H745I-Disco. Setting DDR mode in ConfigSpeedBusOperation will first try and set HighSpeed, which sets the ClockDiv from 8 to 2.

     

    In MMC_HighSpeed(..):

     

    sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC); // = 200 MHz
    // and a few lines later:
    Init.ClockDiv = (sdmmc_clk / (2U * MMC_HIGH_SPEED_FREQ)) + 1U; // = 2

     

    Then, if successful, it will call MMC_DDR_Mode, which calls PwrClassUpdate, which calls MMC_ReadExtCSD. 

    It seems that MMC_ReadExtCSD does not work at this higher speed. Changing the ClockDiv back to 8 in the debugger, I can successfully initialize in DDR mode.
    MMC_DDR_Mode does not set the ClockDiv like MMC_HighSpeed does, so that also needs to be added.

    I might raise an issue/PR on github after I've fixed it for myself.