Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDMMC: DMA is disabled too early to complete transfer when reading from SdCard clocked at 16+ MHz from MSI #90

Open
dima-kapustin opened this issue Jun 27, 2024 · 6 comments
Assignees
Labels
bug Something isn't working hal HAL-LL driver-related issue or pull-request. sdmmc SDMMC-related issue or pull-request

Comments

@dima-kapustin
Copy link

dima-kapustin commented Jun 27, 2024

Describe the set-up

  • Custom board with STM32L431RCTx.
  • CubeIDE 1.15.1, L4 drivers v 1.18.0

Describe the bug
Reading from an SdCard hangs with the following conditions:
SDMMC_DCTRL.DMAEN = 0
SDMMC_DCTRL.DTEN = 0
SDMMC_DCOUNT = 0

DMA_CCR5.EN = 1
DMA_CNDTR5 = 1 (!) which means no transfer complete interrupt will be generated ever

The issue is similar to https://community.st.com/t5/stm32-mcus-products/stm32l4-sd-dma-8-mhz-read-failure-due-to-missing-byte/td-p/385880.

How To Reproduce

  1. Indicate the global behavior of your application project.

SDMMC interface is configured with DMA 2 Channel 5, 1 bit data line
SDMMC clock is sourced form MSI @ 48 MHz, NO devider bypass, HW flow control is ON.
PCLK2 clock is 8.192 MHz and sourced from HSE via PLL

Call HAL_MMC_ReadBlocks_DMA() in a loop to read chunks of 1 or 8 blocks waiting until read completes, i.e. HAL_MMC_GetCardState() returns HAL_MMC_CARD_TRANSFER, after each HAL_MMC_ReadBlocks_DMA() call.

After some successful reads the next reading hangs with the conditions (register values) described above.

  1. The modules that you suspect to be the cause of the problem (Driver, BSP, MW ...).

HAL driver for SDMMC

  1. The use case that generates the problem.
    (see 1)
  2. How we can reproduce the problem.
    (see 1)

Additional context

The root cause is how read completion (SDMMC_STA.DATAEND = 1) is handled in function HAL_MMC_IRQHandler() in stm32l4xx_hal_mmc.c

I did two things in HAL_MMC_IRQHandler() to fix the issue:

  1. Commented out line https://github.com/STMicroelectronics/stm32l4xx_hal_driver/blob/42d324ac323088701270f2c19f328bcad0457c52/Src/stm32l4xx_hal_mmc.c#L1659

As per the reference manual:

-- It is not necessary to clear the enable bit (SDMMC_DCTRL.DTEN) after the end of a data transfer but the SDMMC_DCTRL must be updated to enable a new data transfer

  1. Added if statement so the lines 1709-1736 get executed ONLY for write requests.
      if (((context & MMC_CONTEXT_WRITE_SINGLE_BLOCK) != 0U) || ((context & MMC_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U))
      {
      // lines 1709-1736
      }

The code in lines 1709-1736 is ALSO present in MMC_DMAReceiveCplt() where it should be executed when DMA reports transfer complete for reads.

@dima-kapustin dima-kapustin changed the title SDMMC: DMA is siabled too early to complete transfer when reading from SdCard clocked at 16+ MHz from MSI SDMMC: DMA is disabled too early to complete transfer when reading from SdCard clocked at 16+ MHz from MSI Jun 27, 2024
@ALABSTM ALABSTM added bug Something isn't working hal HAL-LL driver-related issue or pull-request. sdmmc SDMMC-related issue or pull-request labels Jun 28, 2024
@ALABSTM ALABSTM added this to To do in stm32cube-mcu-fw-dashboard via automation Jun 28, 2024
@ALABSTM ALABSTM assigned ALABSTM and KRASTM and unassigned ALABSTM Jun 28, 2024
@KRASTM
Copy link
Contributor

KRASTM commented Jul 1, 2024

Hello @dima-kapustin,

Thank you for the report.
Could you please, share your IOC file or an extract of the config or project, at least for SDMMC and DMA. So, I can use it to reproduce the issue.

In the meanwhile, I will try to analyze your fix or workaround.

With regards.

@dima-kapustin
Copy link
Author

Hi @KRASTM !

Here you go (it is almost standard-generated by CubeIDE):

/**
  * Initializes the Global MSP.
  */
void HAL_MspInit(void)
{

  /* USER CODE BEGIN MspInit 0 */

  /* USER CODE END MspInit 0 */

  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_RCC_PWR_CLK_ENABLE();

  /* System interrupt init*/

  /* USER CODE BEGIN MspInit 1 */

  /* USER CODE END MspInit 1 */
}

/**
* @brief MMC MSP Initialization
* This function configures the hardware resources used in this example
* @param hmmc: MMC handle pointer
* @retval None
*/
void HAL_MMC_MspInit(MMC_HandleTypeDef* hmmc)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hmmc->Instance==SDMMC1)
  {
  /* USER CODE BEGIN SDMMC1_MspInit 0 */
    LL_GPIO_SetOutputPin(SdCard_D0_GPIO_Port, SdCard_D0_Pin);
    LL_GPIO_SetOutputPin(SdCard_CK_GPIO_Port, SdCard_CK_Pin);
    LL_GPIO_SetOutputPin(SdCard_CMD_GPIO_Port, SdCard_CMD_Pin);

    LL_RCC_SetSDMMCClockSource(LL_RCC_SDMMC1_CLKSOURCE_MSI);

    SdCardPowerOnPin::reset();
    LL_mDelay(50);
  /* USER CODE END SDMMC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SDMMC1_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**SDMMC1 GPIO Configuration
    PC8     ------> SDMMC1_D0
    PC12     ------> SDMMC1_CK
    PD2     ------> SDMMC1_CMD
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

    /* SDMMC1 DMA Init */
    /* SDMMC1 Init */
    hdma_sdmmc1.Instance = DMA2_Channel5;
    hdma_sdmmc1.Init.Request = DMA_REQUEST_7;
    hdma_sdmmc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_sdmmc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sdmmc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sdmmc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_sdmmc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_sdmmc1.Init.Mode = DMA_NORMAL;
    hdma_sdmmc1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_sdmmc1) != HAL_OK)
    {
      Error_Handler();
    }

    /* Several peripheral DMA handle pointers point to the same DMA handle.
     Be aware that there is only one channel to perform all the requested DMAs. */
    /* Be sure to change transfer direction before calling
     HAL_SD_ReadBlocks_DMA or HAL_SD_WriteBlocks_DMA. */
    __HAL_LINKDMA(hmmc,hdmarx,hdma_sdmmc1);
    __HAL_LINKDMA(hmmc,hdmatx,hdma_sdmmc1);

    /* SDMMC1 interrupt Init */
    HAL_NVIC_SetPriority(SDMMC1_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
  /* USER CODE BEGIN SDMMC1_MspInit 1 */
    SdCardResetPin::set();
    LL_mDelay(50);
  /* USER CODE END SDMMC1_MspInit 1 */
  }
}
static void MX_SDMMC1_MMC_Init(void)
{

  /* USER CODE BEGIN SDMMC1_Init 0 */
  /* USER CODE END SDMMC1_Init 0 */

  /* USER CODE BEGIN SDMMC1_Init 1 */

  /* USER CODE END SDMMC1_Init 1 */
  hmmc1.Instance = SDMMC1;
  hmmc1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
  hmmc1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
  hmmc1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_ENABLE;
  hmmc1.Init.BusWide = SDMMC_BUS_WIDE_1B;
  hmmc1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE;
  hmmc1.Init.ClockDiv = 0;
  if (HAL_MMC_Init(&hmmc1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SDMMC1_Init 2 */

  /* USER CODE END SDMMC1_Init 2 */

}

@KRASTM
Copy link
Contributor

KRASTM commented Jul 5, 2024

Hello @dima-kapustin,

I tried using the config that you shared, but unfortunately, I didn't manage to reproduce the problem on our board (not the same µc, the L496).
If you have ST board that you can use it to test an example based on your config, or if you can share some screenshots of the debug mode while the problem occurs, it can help us to confirm the issue.

In the other hand, I shared your fix and proposal with our team in order to analyze it and confirm it.

With regards,

@dima-kapustin
Copy link
Author

dima-kapustin commented Jul 5, 2024

Hi @KRASTM!

Thanks for pushing it through!

I am afraid I cannot provide more details about the setup at the moment.

We faced the issue when increased SDMMC clock rate over 16 MHz, i.e. 24 MHz and higher. At clock rates lower than or equal to 16 MHz the issue did not appear for years (!).

I think there is fundamental issue in the irq processing flow:

  • in write direction (DMA -> SDMMC -> SdCars) cleanup shall be done when SDMMC reports end of data transmission

  • in read direction (SdCard -> SDMMC -> DMA) cleanup shall be done after DMA reports end of data transmission.

However, in the current implementation the cleanup is done after SDMMC reports end of data transmission in both directions - write and read. When SDMMC clock is low enough, everything works fine. But as soon as SDMMC clock rate increases (24+ MHz in our case) the cleanup fails because DMA gets disabled before it completes data transfer and reports end of the transmission.

Beat regards,
Dima.

@KRASTM
Copy link
Contributor

KRASTM commented Jul 5, 2024

Hello @dima-kapustin,

There is a section in the datasheet DS11453 page174, about SDMMC characteristics:
Could you have a look at it, and I suggest you change or set the Speed mode into: OSPEEDRy[1:0] = 11, High or very High and give it a try. It may help you.

With regards,

@dima-kapustin
Copy link
Author

dima-kapustin commented Jul 5, 2024

This is one of the first things we tested... No, it does not help with the issue.

Thanks,
Dima.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working hal HAL-LL driver-related issue or pull-request. sdmmc SDMMC-related issue or pull-request
Projects
Development

No branches or pull requests

3 participants