cancel
Showing results for 
Search instead for 
Did you mean: 

BUG: DMA Fatal Error - Ethernet in STM32F407 processor

DMårt
Lead

Hi!

Problem:

I found a bug when I enabling the ETH perpherial in the STM32F407 processor. The error is a DMA bus error.

What's happening:

It occurs when the ethernet PHY recieve a message from my router.

How to produce the error:

I do the following steps to reproduce the error.

  1. I start up my PCB board
  2. I let the initialization do its job and I get no error back.
  3. I plugin my ethernet cable
  4. I ping a random device with a random number

Then this call back function HAL_ETH_ErrorCallback(ETH_HandleTypeDef *heth); will be called.

0693W00000LyBzOQAV.pngThe error code is a DMA error.

/** @defgroup ETH_Error_Code ETH Error Code
  * @{
  */
#define HAL_ETH_ERROR_NONE         ((uint32_t)0x00000000U)   /*!< No error            */
#define HAL_ETH_ERROR_PARAM        ((uint32_t)0x00000001U)   /*!< Busy error          */
#define HAL_ETH_ERROR_BUSY         ((uint32_t)0x00000002U)   /*!< Parameter error     */
#define HAL_ETH_ERROR_TIMEOUT      ((uint32_t)0x00000004U)   /*!< Timeout error       */
#define HAL_ETH_ERROR_DMA          ((uint32_t)0x00000008U)   /*!< DMA transfer error  */
#define HAL_ETH_ERROR_MAC          ((uint32_t)0x00000010U)   /*!< MAC transfer error  */
#if (USE_HAL_ETH_REGISTER_CALLBACKS == 1)
#define HAL_ETH_ERROR_INVALID_CALLBACK ((uint32_t)0x00000020U)    /*!< Invalid Callback error  */
#endif /* USE_HAL_ETH_REGISTER_CALLBACKS */
/**
  * @}
  */

The DMA error is a DMA bus error.

0693W00000LyC0HQAV.pngThis part of the code creates the DMA error code. See the arrow <---- HERE!

/* ETH DMA Error */
  if (__HAL_ETH_DMA_GET_IT(heth, ETH_DMASR_AIS))
  {
    if (__HAL_ETH_DMA_GET_IT_SOURCE(heth, ETH_DMAIER_AISE))
    {
      heth->ErrorCode |= HAL_ETH_ERROR_DMA;
 
      /* if fatal bus error occurred */
      if (__HAL_ETH_DMA_GET_IT(heth, ETH_DMASR_FBES))
      {
        /* Get DMA error code  */
        heth->DMAErrorCode = READ_BIT(heth->Instance->DMASR, (ETH_DMASR_FBES | ETH_DMASR_TPS | ETH_DMASR_RPS)); <<--- HERE!
 
        /* Disable all interrupts */
        __HAL_ETH_DMA_DISABLE_IT(heth, ETH_DMAIER_NISE | ETH_DMAIER_AISE); 
 
        /* Set HAL state to ERROR */
        heth->gState = HAL_ETH_STATE_ERROR;
      }
      else
      {
        /* Get DMA error status  */
        heth->DMAErrorCode = READ_BIT(heth->Instance->DMASR, (ETH_DMASR_ETS | ETH_DMASR_RWTS |
                                                              ETH_DMASR_RBUS | ETH_DMASR_AIS));
 
        /* Clear the interrupt summary flag */
        __HAL_ETH_DMA_CLEAR_IT(heth, (ETH_DMASR_ETS | ETH_DMASR_RWTS |
                                      ETH_DMASR_RBUS | ETH_DMASR_AIS));
      }

 My main function

 
 
/* Private variables ---------------------------------------------------------*/
 
ETH_TxPacketConfig TxConfig;
ETH_DMADescTypeDef  DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef  DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
 
void HAL_ETH_ErrorCallback(ETH_HandleTypeDef *heth){
	uint32_t errorCode = heth->ErrorCode;
}
 
int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_FSMC_Init();
  MX_DCMI_Init();
  MX_SPI2_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  MX_ADC1_Init();
  MX_CAN1_Init();
  MX_RTC_Init();
  MX_TIM4_Init();
  MX_UART5_Init();
  MX_ETH_Init();
  /* USER CODE BEGIN 2 */
 
  /* Start up LCD */
  HAL_GPIO_WritePin(LCD_RESET_GPIO_Port, LCD_RESET_Pin, GPIO_PIN_SET);
  LCD_BL_ON();
  lcdInit();
 
  HAL_GPIO_WritePin(ETH_RESET_GPIO_Port, ETH_RESET_Pin, GPIO_PIN_RESET);
  HAL_Delay(1);
  HAL_GPIO_WritePin(ETH_RESET_GPIO_Port, ETH_RESET_Pin, GPIO_PIN_SET);
 
  /* Enable interrupt */
  HAL_ETH_Start_IT(&heth);
 
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
 
  }
  /* USER CODE END 3 */
}

Hardware settings

The hardware settings are for RMII for the Ethernet PHY DP83848 .

Yes! The LED D1 flashes when something happen at the network. The pin ACT_LED/COL should go low when something happens. The oscillator is at 50 MHz and very close to the DP83848 chip.

0693W00000LyNQVQA3.png0693W00000LyNQaQAN.pngSoftware settings:

Download my project here:

STM32 project:

https://easyupload.io/xhrn8d

Schematic project (KiCAD):

https://easyupload.io/9x3xig

Why I'm thinking this be a bug?

Because I have not configured ETH DMA and it give me a bug about that when my Ethernet PHY got a message and pass it over to the STM32 processor. I assume that STM32CubeIDE 1.9.0 have some issues then.

What am I 100% sure that I have been constructed the hardware correctly?

The Ethernet PHY address is 0x1 and I have been veryfied that this is correct address. The LED D1 is flashing when activity occurs at the network.

The callback function calls when the LED D1 flashes after initialization.

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-Computer
46 REPLIES 46

@Daniel Mårtensson​ Sorry, I don't understand to which direction you're going now.

Looks like you want to use LwIP.

That github project is 5 years old, so it is based on pre-"rework" ETH driver version.

Do you use a compatible older STM32Cube_F4 library package containing the ETH driver and older LwIP version as well?

The github author has no documentation or even mention for which board this project is for.

At least, it's the time to run wireshark and see the packets on the wire.

I don't know either. I just want it to work. I can do this commando. The MAC-address and IP-address is 100% correct. So it seems that I can set the address at the ST-processor. But I don't know if the LwIP software contains bugs so it cannot understand the message from the router? Or the TX part of LwIP is broken?

No. I did use the latest compiler for the F4-series. I will analyze with wireshark today! I give you a reply later.

Do you have a F4 project that works with LwIP? If you can upload it here and I can test it?

C:\Users\danie>arp -a 192.168.1.15
 
Interface: 192.168.1.34 --- 0xc
  Internet Address      Physical Address      Type
  192.168.1.15          80-80-a2-ae-13-41     dynamic
 
C:\Users\danie>

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-Computer

@Pavel A.​ 

@Piranha​ 

I found an issue now. This function calls with DevAddr = 0x1, RegAddr = 0x0 and RegVal = 0x8000 which means that RESET is called. That's correct. But this function ONLY calls at the start of the ST-controller. Otherwise...the function is not called any more during the run.

/**
  * @brief  Write a value to a PHY register through the MDIO interface.
  * @param  DevAddr: PHY port address
  * @param  RegAddr: PHY register address
  * @param  RegVal: Value to be written
  * @retval 0 if OK -1 if Error
  */
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal)
{
  if(HAL_ETH_WritePHYRegister(&heth, DevAddr, RegAddr, RegVal) != HAL_OK)
  {
    return -1;
  }
 
  return 0;
}

0693W00000LzFVFQA3.png

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-Computer

@Pavel A.​ 

@Piranha​ 

Got an error message now! Se the arrow down below.

Notice that this function return HAL_OK only once at the startup of the F407 processorn. Then the while-loop runs OK. But that's ONLY once it will happen. The rest of the times, the function returns HAL_ERROR.

Can this be a bug?

@Imen DAHMEN

/**
  * @brief  Read a received packet.
  * @param  heth: pointer to a ETH_HandleTypeDef structure that contains
  *         the configuration information for ETHERNET module
  * @param  pAppBuff: Pointer to an application buffer to receive the packet.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_ETH_ReadData(ETH_HandleTypeDef *heth, void **pAppBuff)
{
  uint32_t descidx;
  ETH_DMADescTypeDef *dmarxdesc;
  uint32_t desccnt = 0U;
  uint32_t desccntmax;
  uint32_t bufflength;
  uint8_t rxdataready = 0U;
 
 
  if (pAppBuff == NULL)
  {
    heth->ErrorCode |= HAL_ETH_ERROR_PARAM;
    return HAL_ERROR;
  }
 
  if (heth->gState != HAL_ETH_STATE_STARTED)
  {
    return HAL_ERROR;
  }
 
  descidx = heth->RxDescList.RxDescIdx;
  dmarxdesc = (ETH_DMADescTypeDef *)heth->RxDescList.RxDesc[descidx];
  desccntmax = ETH_RX_DESC_CNT - heth->RxDescList.RxBuildDescCnt;
 
  /* Check if descriptor is not owned by DMA */
  while ((READ_BIT(dmarxdesc->DESC0, ETH_DMARXDESC_OWN) == (uint32_t)RESET) && (desccnt < desccntmax)
         && (rxdataready == 0U))
  {
    if (READ_BIT(dmarxdesc->DESC0,  ETH_DMARXDESC_LS)  != (uint32_t)RESET)
    {
      /* Get timestamp high */
      heth->RxDescList.TimeStamp.TimeStampHigh = dmarxdesc->DESC6;
      /* Get timestamp low */
      heth->RxDescList.TimeStamp.TimeStampLow  = dmarxdesc->DESC7;
    }
    if ((READ_BIT(dmarxdesc->DESC0, ETH_DMARXDESC_FS) != (uint32_t)RESET) || (heth->RxDescList.pRxStart != NULL))
    {
      /* Check first descriptor */
      if (READ_BIT(dmarxdesc->DESC0, ETH_DMARXDESC_FS) != (uint32_t)RESET)
      {
        heth->RxDescList.RxDescCnt = 0;
        heth->RxDescList.RxDataLength = 0;
      }
 
      /* Check if last descriptor */
      bufflength = heth->Init.RxBuffLen;
      if (READ_BIT(dmarxdesc->DESC0, ETH_DMARXDESC_LS) != (uint32_t)RESET)
      {
        /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */
        bufflength = ((dmarxdesc->DESC0 & ETH_DMARXDESC_FL) >> ETH_DMARXDESC_FRAMELENGTHSHIFT) - 4U;
 
        /* Save Last descriptor index */
        heth->RxDescList.pRxLastRxDesc = dmarxdesc->DESC0;
 
        /* Packet ready */
        rxdataready = 1;
      }
 
      /* Link data */
      WRITE_REG(dmarxdesc->BackupAddr0, dmarxdesc->DESC2);
#if (USE_HAL_ETH_REGISTER_CALLBACKS == 1)
      /*Call registered Link callback*/
      heth->rxLinkCallback(&heth->RxDescList.pRxStart, &heth->RxDescList.pRxEnd,
                           (uint8_t *)dmarxdesc->BackupAddr0, bufflength);
#else
      /* Link callback */
      HAL_ETH_RxLinkCallback(&heth->RxDescList.pRxStart, &heth->RxDescList.pRxEnd,
                             (uint8_t *)dmarxdesc->BackupAddr0, (uint16_t) bufflength);
#endif  /* USE_HAL_ETH_REGISTER_CALLBACKS */
      heth->RxDescList.RxDescCnt++;
      heth->RxDescList.RxDataLength += bufflength;
 
      /* Clear buffer pointer */
      dmarxdesc->BackupAddr0 = 0;
    }
 
    /* Increment current rx descriptor index */
    INCR_RX_DESC_INDEX(descidx, 1U);
    /* Get current descriptor address */
    dmarxdesc = (ETH_DMADescTypeDef *)heth->RxDescList.RxDesc[descidx];
    desccnt++;
  }
 
  heth->RxDescList.RxBuildDescCnt += desccnt;
  if ((heth->RxDescList.RxBuildDescCnt) != 0U)
  {
    /* Update Descriptors */
    ETH_UpdateDescriptor(heth);
  }
 
  heth->RxDescList.RxDescIdx = descidx;
 
  if (rxdataready == 1U)
  {
    /* Return received packet */
    *pAppBuff = heth->RxDescList.pRxStart;
    /* Reset first element */
    heth->RxDescList.pRxStart = NULL;
 
    return HAL_OK;
  }
 
  /* Packet not ready */
  return HAL_ERROR; <<------------------ HERE!
}

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-Computer
Pavel A.
Evangelist III

@Daniel Mårtensson​ Sorry I'm a bit confused now. Do you suspect PHY?

From your last reply it looks that RX event (callback) scans RX descriptors but cannot find a complete packet to return.

Can there be a bug somewhere? Maybe, but it is hard to debug here in the forum. Do you have wireshark captures?

I do not suspect PHY. I think PHY is correct designed.

I think it's a STM32 software issue.

I can recieve data, but I cannot send data.

So I assume that RX is OK, but TX is never called, except at the start up.

I did a double check and the RX descriptors works when I ping the ST controller.

But even if I ping. No TX function or "Transmit to PHY" is called. Why?

Only "Read from PHY" and other RX-callbacks is called.

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-Computer
Pavel A.
Evangelist III

It looks like "reworked" ETH driver for F4 series needs thorough fixing.

The incoming ping request is corrupted, thus no response.

https://community.st.com/s/question/0D53W00001WIg4aSAD/ethernet-hal-driver-cubemx-65-and-stm32f4-broken-icmp-ping-anyone-knows-how-to-fix

Yes! It seems that F4 series with STM32CubeIDE 1.9.0 have some bugs when it comes to LwIP.

How can we confirm that for the ST-team here at this forum?

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-Computer

This work is already done by @Piranha​ :gem_stone:

Let's hope it will be fixed timely (until we retire)

So there is a bug in STM32CubeIDE 1.9.0 for the F4 series about LwIP and it has been reported to the ST-developer team and the ST-developer team is aware of it and they have given the community members a ticket number of the process of the future fix?

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-Computer