cancel
Showing results for 
Search instead for 
Did you mean: 

BUG: DMA Fatal Error - Ethernet in STM32F407 processor

DMårt
Senior III

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.

46 REPLIES 46

@Piranha​ I'm not involved in FreeRTOS+ eth driver development. We finally went to roll our own on 'H7. Perhaps will regret it later and return to LwIP.

> I'm not involved in FreeRTOS+ eth driver development.

I mean those, who can be seen in commits.

> ...our own on 'H7. ...and return to LwIP.

Your own IP stack? Why? Or do you mean your own driver for some available IP stack?

I can Ping now...but I get a time out response.

I upload my project (Eth + LwIP): https://easyupload.io/o6ws85 

I enable LwIP and it works...sort of.

When I ping it looks like this:

C:\Users\danie>ping 192.168.1.15
 
Pinging 192.168.1.15 with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.
 
Ping statistics for 192.168.1.15:
    Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),
 
C:\Users\danie>

That means my STM32 processor gets a message when I ping the processor.

But the STM32 processor do not send anything back. Why?

These callback functions in ethernetif.c is called when I ping my processor.

void HAL_ETH_RxAllocateCallback(uint8_t **buff)
{
/* USER CODE BEGIN HAL ETH RxAllocateCallback */
 
  struct pbuf_custom *p = LWIP_MEMPOOL_ALLOC(RX_POOL);
  if (p)
  {
    /* Get the buff from the struct pbuf address. */
    *buff = (uint8_t *)p + offsetof(RxBuff_t, buff);
    p->custom_free_function = pbuf_free_custom;
    /* Initialize the struct pbuf.
    * This must be performed whenever a buffer's allocated because it may be
    * changed by lwIP or the app, e.g., pbuf_free decrements ref. */
    pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, p, *buff, ETH_RX_BUFFER_SIZE);
  }
  else
  {
    RxAllocStatus = RX_ALLOC_ERROR;
    *buff = NULL;
  }
/* USER CODE END HAL ETH RxAllocateCallback */
}
 
void HAL_ETH_RxLinkCallback(void **pStart, void **pEnd, uint8_t *buff, uint16_t Length)
{
/* USER CODE BEGIN HAL ETH RxLinkCallback */
 
  struct pbuf **ppStart = (struct pbuf **)pStart;
  struct pbuf **ppEnd = (struct pbuf **)pEnd;
  struct pbuf *p = NULL;
 
  /* Get the struct pbuf from the buff address. */
  p = (struct pbuf *)(buff - offsetof(RxBuff_t, buff));
  p->next = NULL;
  p->tot_len = 0;
  p->len = Length;
 
  /* Chain the buffer. */
  if (!*ppStart)
  {
    /* The first buffer of the packet. */
    *ppStart = p;
  }
  else
  {
    /* Chain the buffer to the end of the packet. */
    (*ppEnd)->next = p;
  }
  *ppEnd  = p;
 
  /* Update the total length of all the buffers of the chain. Each pbuf in the chain should have its tot_len
   * set to its own length, plus the length of all the following pbufs in the chain. */
  for (p = *ppStart; p != NULL; p = p->next)
  {
    p->tot_len += Length;
  }
 
/* USER CODE END HAL ETH RxLinkCallback */
}
 
void HAL_ETH_TxFreeCallback(uint32_t * buff)
{
/* USER CODE BEGIN HAL ETH TxFreeCallback */
 
  pbuf_free((struct pbuf *)buff);
 
/* USER CODE END HAL ETH TxFreeCallback */
}

Piranha
Chief II

Read my post in this topic:

https://community.st.com/s/question/0D53W00001VYkb5SAD/stm32f417-lwip-udp-packets-are-being-truncated

The new rewritten ETH driver for F4 is basically dysfunctional. Revert to the legacy one...

And also look at this:

https://community.st.com/s/question/0D50X0000BOtfhnSQB/how-to-make-ethernet-and-lwip-working-on-stm32

@Piranha​ sent you a PM.

@Daniel Mårtensson​ This means now you don't crash on RX - so the RX buffers and descriptors are much better. Now you can continue to get the LwIP example working (test receive and reply to ARPs, sending your own ARPs and sending reply, getting DHCP address if you want to, then ping, udp or tcp... - or remove LwIP and proceed with sending and receiving "pure" ethernet packets.

@Pavel A.​  Yes. RX seems to work because the callback functions runs when the processor gets a message.

I have problems to import these ST-made projects into STM32CubeIDE. I don't know how to import it. I know how to import .ioc projects. But not MDK-ARM, SW4STM32 etc.

I'm also don't know which example I should use. Let's say that I'm using the LwIP_TCP_Echo_Client example for STM32F4xG-Eval.

  1. Can I use it for STM32F407VBT?
  2. How can I import that project so it become a STM32 project in STM32CubeIDE? I did try to import an ac6 example project, but I did not found any .ioc file that can configure my project. It was also difficult to compile.

Pavel A.
Evangelist III

> I have problems to import these ST-made projects into STM32CubeIDE. I don't know how to import it.  I know how to import .ioc projects. But not MDK-ARM, SW4STM32 etc.

Unfortunately this is... complicated. Examples for old STM32 lines like F4 have been created before CubeMX and CubeIDE, so there are no .ioc files and no native CubeIDE projects. One has to import SW4STM32/ac6 project to CubeIDE (look for this item in CubeIDE import options menu).

The import is not perfect, it can mess some things such as linker script - which is very important for ethernet projects; check that important details are not lost after import and restore if needed.

Linker scripts of CubeIDE and SW4STM are practically same, as both are based on the same GCC toolchain type.

As for missing ioc file, it is simply not there. Have to re-create it, or google, or persuade someone else to make it for you. Unless you decide to write the initialization code yourself without the Cube generator.

> I'm also don't know which example I should use. 

You want only "pure" ethernet packets - so take only the buffer RAM allocation from any LwIP example and throw away all the rest.

Unfortunately again, after the "ETH driver rework" this RX buffer allocation is wrapped in confusing callbacks and LwIP headers. Basically you keep one big enough memory buffer per RX descriptor and put address of each buffer into the descriptor before starting the ETH.

 [CORRECTION] what happens in the RX callback in "reworked" version is allocation of LwIP packet descriptors, not the buffers. The buffers must be assigned before RX occurs.

Pavel A.
Evangelist III

@Piranha​ Your suggested fix to FreeRTOS+ STM32xx eth driver has been implemented and merged on github :dizzy: . Amazon is speedy )

https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/pull/458

https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/pull/459#event-6492385210

Hi! I tried this project, it fits my processor. But it did not work for me. Still "Time out" when I ping it.

DeeFuse/STM32F4-liwp: STM32F4 lwip Ethernet with easy swtiching between static IP and DHCP (github.com)