cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7, Ethernet, LWIP and STM32CubeH7 V1.4.0, how?

alexey_public
Associate II

I reading all topics about ethernet problems in Cube H7 1.1-1.3.

And run example LwIP_HTTP_Server_Netconn_RTOS - it working good.

But - I can't to repeat this example.

I using NUCLEO-H743ZI board.

Creating project from STMCUbe, enabling LWIP with static IP, modifing STM32H743ZITX_FLASH.ld script from example by adding this :

  .lwip_sec (NOLOAD) : {
    . = ABSOLUTE(0x30040000);
    *(.RxDecripSection) 
    
    . = ABSOLUTE(0x30040060);
    *(.TxDecripSection)
    
    . = ABSOLUTE(0x30040200);
    *(.RxArraySection) 
  } >RAM_D2 AT> FLASH
  

Modify MPU Settings as in example:

void MPU_Config(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct = {0};
 
  /* Disables the MPU */
  HAL_MPU_Disable();
  /** Initializes and configures the Region and the memory to be protected 
  */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x30040000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256B;
  MPU_InitStruct.SubRegionDisable = 0x0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
 
  HAL_MPU_ConfigRegion(&MPU_InitStruct);
  /** Initializes and configures the Region and the memory to be protected 
  */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER1;
  MPU_InitStruct.BaseAddress = 0x30044000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
  MPU_InitStruct.SubRegionDisable = 0x0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
 
  HAL_MPU_ConfigRegion(&MPU_InitStruct);
  /* Enables the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
 
}

Modify main:

  /* USER CODE BEGIN WHILE */
  while (1)
  	  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
 
	  ethernetif_input(&gnetif);
 
	  sys_check_timeouts();
 
  	  }
  /* USER CODE END 3 */

Building and RUN - and it doesn't work..

I try using MPU init code from this topic

https://community.st.com/s/article/FAQ-Ethernet-not-working-on-STM32H7x3?t=1562229776524

void MPU_Config(void)
{
	MPU_Region_InitTypeDef MPU_InitStruct;
 
	/* Disables the MPU */
	HAL_MPU_Disable();
	/**Initializes and configures the Region and the memory to be protected
	*/
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER2;
	MPU_InitStruct.BaseAddress = 0x30040000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_256B;
	MPU_InitStruct.SubRegionDisable = 0x0;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
	/**Initializes and configures the Region and the memory to be protected
	*/
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER1;
	MPU_InitStruct.BaseAddress = 0x30044000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
	MPU_InitStruct.SubRegionDisable = 0x0;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
	MPU_InitStruct.Enable = MPU_REGION_ENABLE;
	MPU_InitStruct.Number = MPU_REGION_NUMBER0;
	MPU_InitStruct.BaseAddress = 0x30040000;
	MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
	MPU_InitStruct.SubRegionDisable = 0x0;
	MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
	MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
	MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
	MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
	MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
	MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
 
	HAL_MPU_ConfigRegion(&MPU_InitStruct);
 
	/* Enables the MPU */
	HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

It doesn't work too.

So, I try to create the working example with FreeRTOS.

Creating project from Cube, modify STM32H743ZITX_FLASH.ld, modify MPU settings, build project - and it doesn't work :(

I using DHCP and static IP, and can't to ping this board in all variant, working only LwIP_HTTP_Server_Netconn_RTOS sample poject.

But this sample project without ioc file, I can't create my own project based on this sample code.

May be exists working ioc file with LWIP ethernet without FreeRTOS? And ld script for it?

8 REPLIES 8
alexey_public
Associate II

I found good working example on HAL H7 V1.3.0:

https://github.com/MX-Master/STM32H7_Nucleo-H743ZI_Ethernet_LwIP

But can't to move it to new version of stm cube.

After ioc file updating and project regeneration this project can't to work properly.

Checking all critical places of code by readme file get full coincidence.

And can't to open this ioc file without updating - because STM Cube hangs and does not respond.

STM Cube work properly only with updating this ioc file to new version.

alexey_public
Associate II

Working!

For the first - needed all necessary settings from this project - readme file:

https://github.com/MX-Master/STM32H7_Nucleo-H743ZI_Ethernet_LwIP

For the second HAL H7 v1.4.0 file ethernetif.c needing this simple modification:

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
  uint32_t i=0, framelen = 0;
  struct pbuf *q;
  err_t errval = ERR_OK;
  ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT];
  
  memset(Txbuffer, 0 , ETH_TX_DESC_CNT*sizeof(ETH_BufferTypeDef));
  
  for(q = p; q != NULL; q = q->next)
  {
    if(i >= ETH_TX_DESC_CNT)	
      return ERR_IF;
    
    Txbuffer[i].buffer = q->payload;
    Txbuffer[i].len = q->len;
    framelen += q->len;
    
    if(i>0)
    {
      Txbuffer[i-1].next = &Txbuffer[i];
    }
    
    if(q->next == NULL)
    {
      Txbuffer[i].next = NULL;
    }
    
    i++;
  }
 
  TxConfig.Length = framelen;
  TxConfig.TxBuffer = Txbuffer;
 
  SCB_CleanInvalidateDCache();
 
  HAL_ETH_Transmit(&heth, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);
  
  return errval;
}
 
static struct pbuf * low_level_input(struct netif *netif)
{
  struct pbuf *p = NULL;
  ETH_BufferTypeDef RxBuff;
  uint32_t framelength = 0;
  struct pbuf_custom* custom_pbuf;
  
  
  if (HAL_ETH_IsRxDataAvailable(&heth))
  {
	  SCB_CleanInvalidateDCache();
 
	HAL_ETH_GetRxDataBuffer(&heth, &RxBuff);
    HAL_ETH_GetRxDataLength(&heth, &framelength);
    
    /* Build Rx descriptor to be ready for next data reception */
    HAL_ETH_BuildRxDescriptors(&heth);
 
#if defined(DUAL_CORE) && defined(CORE_CM7) 
    /* Invalidate data cache for ETH Rx Buffers */
    SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff.buffer, framelength);
#endif    
    
    custom_pbuf  = (struct pbuf_custom*)LWIP_MEMPOOL_ALLOC(RX_POOL);
    custom_pbuf->custom_free_function = pbuf_free_custom;
    
    p = pbuf_alloced_custom(PBUF_RAW, framelength, PBUF_REF, custom_pbuf, RxBuff.buffer, ETH_RX_BUFFER_SIZE);
    
    return p;
  }
  else
  {
    return NULL;
  }
}

Only inserting this code row:

  HAL_ETH_Transmit(&heth, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);

in two places.

This is all.

All will be good working with Nucleo-H743ZI.

And I tested this code with Apollo H743 development board, with LAN8720A PHY, good working too, start code for this board:

  /* USER CODE BEGIN 2 */
 
  SetLED0(1);
 
  PCF8574_Init(pPCF8574, &hi2c2, false, 0, 0, 0);
  PCF8574_SetDir(pPCF8574, 0b11000101);
 
  SetETHReset(0);
  HAL_Delay(100);
 
  SetETHReset(1);
  HAL_Delay(100);
 
  MX_LWIP_Init();
 
  SetLED1(1);
 
  HAL_Delay(1000);
 
  /* USER CODE END 2 */

This board using PCF8574 for resetting PHY. That's why MX_LWIP_Init() must be selected in project manager - advanced settings - not generate function call.

And call manually after PHY reset finished by PCF8574.

alexey_public
Associate II

One moment - in project https://github.com/MX-Master/STM32H7_Nucleo-H743ZI_Ethernet_LwIP in MPU Settings Region 1 was set as

 MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;

This is not right for normal working (but with simple ICMP query all OK).

Normal setting - FULL_ACCESS.

MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;

alexey_public
Associate II

And PHY Pin Speed can be set as Very HIGH (not only High):

.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

Piranha
Chief II
SCB_CleanInvalidateDCache();

Cleaning all D-cache, damages data that's changed by DMA - descriptors and Rx buffers.

Invalidating all D-cache damages data that's changed by CPU - any cached (but not written to RAM) data.

This is a very popular flaw in general and ST's code is also full of it...

HAL 1.4.0 removed "SCB_CleanInvalidateDCache()" from "low_level_output" and "low_level_input" but for my board, lwip became slow and instable. Adding the function again, worked fine...

Actually, the code snippets described "good working" above would not work reliably, and would increasingly fail with increasing packet rate and possibly size.

These are problems with low_level_input alone just by inspection:

  1. Bug design, bug implementation: The rx buffers are hard-wired to the rx descriptors, and calling HAL_ETH_BuildRxDescriptors re-enables the descriptors so DMA may write the buffer again _before_ your app's finished with it.
  2. Bug, buried prerequisite, bug: The custom_pbuf accommodates one buffer. So ETH_RX_BUFFER_SIZE must be >= the physical MTU. If ETH_RX_BUFFER_SIZE were smaller, the pbuf would need to chain up to N * ETH_RX_DESC_CNT where N = the number of buffers per descriptor, ranging 1 or 2. NB the ETH driver purports to handle 2 buffers per descriptor, but by inspection, only 1 buffer would work.
  3. Inefficiency: SCB_CleanInvalidateDCache cleans entire D-cache.
  4. Inefficiency, bug: RxBuff is a throw-away intermediate structure to used extract a buffer or chain of buffers from HAL_ETH_GetRxDataBuffer, costing cycles every packet. Its next pointer's uninitialised.
  5. Inefficiency: custom_pbuf to transfer ST's RxBuffs to lwIP's pbuf.

Applies FW_H7 v1.4.0, v1.5.0 and v1.6.0.

My documented inspection of ST's H7 ETH code plus coded bug-fixes and efficiency improvements are at https://community.st.com/s/question/0D50X0000C6eNNSSQ2/bug-fixes-stm32h7-ethernet. My code is for a custom board and you'd need some expertise to port it. If you use my code please post your success or problems there to assist other developers.

HLe.2
Associate II

Hi alexey_public.

I can't read "For the first - needed all necessary settings from this project - readme file:"

Can you re-upload?

I create ETH Project with STM32CubeMxIde follow steps above, but it doesn't work.