Ethernet transceiver of STM32H750 HAL Library

problem

Recently, after debugging STM32H750+LAN8720, LwIP has been transplanted for most of the day. ping can also pass through, and TCP test is also successful. Originally thought that the HAL Library of ST was finally relieved. As a result, I optimized the compilation to the maximum
... it's impossible to ping directly. Later, it was found that there was a big problem with the HAL library. (sure enough, HAL library still doesn't worry. The generated code can only be used for initialization)

Later, it was found that HAL library has two hidden dangers:

  1. There is a problem with the processing of descriptors
  2. Because the single chip microcomputer is Cortex-M7, with Cache and single chip microcomputer, it will execute out of order and access memory in out of order. Out of order access has great hidden dangers for sending / receiving descriptor operation

These problems will be described in detail later

reason

Problem 1 hidden danger handling method of Hal Library

I feel that there seems to be a problem with the way the HAL library handles data descriptors, such as

	SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN);//The descriptor permission is given to ETH here

    if(dmarxdesclist->ItMode != 0U)
    {
      SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_IOC);//The descriptors are given to ETH, but they have to be modified
    }

This is stm32h7xx_ hal_ In eth. C
HAL_StatusTypeDef HAL_ETH_BuildRxDescriptors(ETH_HandleTypeDef *heth)
In my opinion, the owner of the descriptor should be set last, because it marks whether the descriptor is currently owned by eth or by the user (CPU and other gadgets). However, from this code, it actually modifies the descriptor after returning the descriptor to eth. Is this to take advantage of eth's inattention? If so, ETH can really run normally! But this is undoubtedly a security risk. A direct manifestation is that I turn on the compilation optimization (- O3) and there is a problem with Ethernet communication.

And this kind of operation can be seen everywhere in HAL library, which is so terrible
People who estimate ST think that writing eth - > dmacrdtpr or eth - > dmactdtpr (used to tell eth descriptor has been updated) descriptor will take effect, but I read the description of the document. If the application can always update the descriptor, ETH will send it again even if it does not write this register, that is, setting the OWN bit of the descriptor to 1 means that the descriptor belongs to eth, The user should not modify the water splashed out by the sent descriptor until the OWN is cleared by eth. What is the use of eth - > dmacrdtpr or eth - > dmactdtpr? In order to reduce the occupation of eth on the bus, if eth cannot detect a valid descriptor (all OWN are 0), ETH will no longer access memory. Therefore, a mechanism is needed to tell eth that the descriptor is updated. It's time to work.
This mechanism is to write eth - > dmacrdtpr (receive descriptor) or eth - > dmactdtpr (send descriptor). These two registers are not only used to mark the address of the last descriptor, but also used to tell the ETH descriptor that it has been updated.

Problem 2 hidden trouble of Hal library 2 does not deal with the disordered access and Cache of Cortex-M7

Normally, the out of order execution of the CPU will not affect the operation of peripherals. Because the memory type of the peripheral register is device by default (and a similar attribute is strongly ordered), this means that no matter how disordered the execution is, it will ensure the correct order (execution order in the code) access to these memories.
But coincidentally, the storage location of the descriptor is not in these memory areas. It can only be placed in the SRAM of domain D3 (ETH's hand can only touch these places), and the memory type of these SRAM is Normal by default. For efficiency, the CPU will access the memory of these areas in disorder (in this way, the key OWN bits may be written in advance or later), which is certainly not what we want.

When the Cache is enabled, this will become more complex, and the written data may not be actually written to memory.

resolvent

For question 1, there is not much code for descriptor operation, so I manually modified stm32h7xx_ hal_ The relevant codes in eth. C (the modified parts will be released later) to ensure that the order of the codes is no problem. It mainly modifies those related to OWN and eth - > dmacrdtpr and eth - > dmactdtpr.

For question 2, I deal with it as follows:

  • Configure MPU, configure the memory area where the descriptor and data Cache are located as buffer and Cache, and configure TEX as LEVEL1(0x01), so that these memories are configured as Normal, but are no longer affected by Cache (this ensures a performance balance. For cortex M7, there are too many steps to Clean the Cache. Although an inline function is called, the register is written once to Clean a Cache line. The whole Cache needs 512 write operations and other related calculations. It is not like not caching the memory in these places, but using the Normal attribute can speed up the memory access speed (it seems that it can package multiple consecutive single byte accesses into 32Bit accesses, which should be due to out of order access. This feature is found when I debug SDRAM to test its speed. Even if Cache is not enabled, the access speed configured as Normal is faster than that of Device.)
  • Add the _DMB() memory isolation instruction before and after the set / reset OWN operation (it should be capitalized and has its OWN compilation isolation to prevent this instruction from being rearranged by the compiler. Unexpectedly, the compiler will also disrupt your instructions)

Modified code

Just modify the code of stm32h7xx_hal_eth.c

Sent modifications

Three functions related to sending have been modified

static uint32_t ETH_Prepare_Tx_Descriptors(ETH_HandleTypeDef *heth, ETH_TxPacketConfig *pTxConfig, uint32_t ItMode)
{
  ETH_TxDescListTypeDef *dmatxdesclist = &heth->TxDescList;
  uint32_t descidx = dmatxdesclist->CurTxDesc;
  uint32_t firstdescidx = dmatxdesclist->CurTxDesc;
  uint32_t descnbr = 0, idx;
  ETH_DMADescTypeDef *dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];

  ETH_BufferTypeDef  *txbuffer = pTxConfig->TxBuffer;
  uint32_t           bd_count = 0;

  /* Current Tx Descriptor Owned by DMA: cannot be used by the application  */
  if((READ_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCWBF_OWN) == ETH_DMATXNDESCWBF_OWN) || (dmatxdesclist->PacketAddress[descidx] != NULL))
  {
    return HAL_ETH_ERROR_BUSY;
  }

  /***************************************************************************/
  /*****************    Context descriptor configuration (Optional) **********/
  /***************************************************************************/
  /* If VLAN tag is enabled for this packet */
  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_VLANTAG) != 0U)
  {
    /* Set vlan tag value */
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXCDESC_VT, pTxConfig->VlanTag);
    /* Set vlan tag valid bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_VLTV);
    /* Set the descriptor as the vlan input source */
    SET_BIT(heth->Instance->MACVIR, ETH_MACVIR_VLTI);

    /* if inner VLAN is enabled */
    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_INNERVLANTAG) != 0U)
    {
      /* Set inner vlan tag value */
      MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXCDESC_IVT, (pTxConfig->InnerVlanTag << 16));
      /* Set inner vlan tag valid bit */
      SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_IVLTV);

      /* Set Vlan Tag control */
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXCDESC_IVTIR, pTxConfig->InnerVlanCtrl);

      /* Set the descriptor as the inner vlan input source */
      SET_BIT(heth->Instance->MACIVIR, ETH_MACIVIR_VLTI);
      /* Enable double VLAN processing */
      SET_BIT(heth->Instance->MACVTR, ETH_MACVTR_EDVLP);
    }
  }

  /* if tcp segmentation is enabled for this packet */
  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U)
  {
    /* Set MSS value */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXCDESC_MSS, pTxConfig->MaxSegmentSize);
    /* Set MSS valid bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_TCMSSV);
  }

  if((READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_VLANTAG) != 0U)|| (READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U))
  {
    /* Set as context descriptor */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_CTXT);
    /* Set own bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_OWN);
    /* Increment current tx descriptor index */
    INCR_TX_DESC_INDEX(descidx, 1U);
    /* Get current descriptor address */
    dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];

    descnbr += 1U;

    /* Current Tx Descriptor Owned by DMA: cannot be used by the application  */
    if(READ_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCWBF_OWN) == ETH_DMATXNDESCWBF_OWN)
    {
      dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[firstdescidx];
      /* Clear own bit */
      CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXCDESC_OWN);

      return HAL_ETH_ERROR_BUSY;
    }
  }

  /***************************************************************************/
  /*****************    Normal descriptors configuration     *****************/
  /***************************************************************************/

  descnbr += 1U;

  /* Set header or buffer 1 address */
  WRITE_REG(dmatxdesc->DESC0, (uint32_t)txbuffer->buffer);
  /* Set header or buffer 1 Length */
  MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B1L, txbuffer->len);

  if(txbuffer->next != NULL)
  {
    txbuffer = txbuffer->next;
    /* Set buffer 2 address */
    WRITE_REG(dmatxdesc->DESC1, (uint32_t)txbuffer->buffer);
    /* Set buffer 2 Length */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, (txbuffer->len << 16));
  }
  else
  {
    WRITE_REG(dmatxdesc->DESC1, 0x0);
    /* Set buffer 2 Length */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, 0x0U);
  }

  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U)
  {
    /* Set TCP Header length */
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_THL, (pTxConfig->TCPHeaderLen << 19));
    /* Set TCP payload length */
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TPL, pTxConfig->PayloadLen);
    /* Set TCP Segmentation Enabled bit */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TSE);
  }
  else
  {
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FL, pTxConfig->Length);

    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CSUM) != 0U)
    {
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CIC, pTxConfig->ChecksumCtrl);
    }

    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CRCPAD) != 0U)
    {
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CPC, pTxConfig->CRCPadCtrl);
    }
  }

  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_VLANTAG) != 0U)
  {
    /* Set Vlan Tag control */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_VTIR, pTxConfig->VlanCtrl);
  }

  /* Mark it as First Descriptor */
  SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FD);
  /* Mark it as NORMAL descriptor */
  CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CTXT);

  /* If source address insertion/replacement is enabled for this packet */
  if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_SAIC) != 0U)
  {
    MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_SAIC, pTxConfig->SrcAddrCtrl);
  }

  /* only if the packet is split into more than one descriptors > 1 */
  while (txbuffer->next != NULL)
  {
    /* Clear the LD bit of previous descriptor */
    CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_LD);
    __DMB();//Modify!!
    /* set OWN bit of Last descriptor */
    SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN);
    __DMB();//Modify!!

    /* Increment current tx descriptor index */
    INCR_TX_DESC_INDEX(descidx, 1U);
    /* Get current descriptor address */
    dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];

    /* Clear the FD bit of new Descriptor */
    CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FD);

    /* Current Tx Descriptor Owned by DMA: cannot be used by the application  */
    if((READ_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN) == ETH_DMATXNDESCRF_OWN) || (dmatxdesclist->PacketAddress[descidx] != NULL))
    {
      descidx = firstdescidx;
      dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];

      /* clear previous desc own bit */
      for(idx = 0; idx < descnbr; idx ++)
      {
        __DMB();//Modify!!
        CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN);
        __DMB();//Modify!!

        /* Increment current tx descriptor index */
        INCR_TX_DESC_INDEX(descidx, 1U);
        /* Get current descriptor address */
        dmatxdesc = (ETH_DMADescTypeDef *)dmatxdesclist->TxDesc[descidx];
      }

      return HAL_ETH_ERROR_BUSY;
    }

    descnbr += 1U;

    /* Get the next Tx buffer in the list */
    txbuffer = txbuffer->next;

    /* Set header or buffer 1 address */
    WRITE_REG(dmatxdesc->DESC0, (uint32_t)txbuffer->buffer);
    /* Set header or buffer 1 Length */
    MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B1L, txbuffer->len);

    if (txbuffer->next != NULL)
    {
      /* Get the next Tx buffer in the list */
      txbuffer = txbuffer->next;
      /* Set buffer 2 address */
      WRITE_REG(dmatxdesc->DESC1, (uint32_t)txbuffer->buffer);
      /* Set buffer 2 Length */
      MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, (txbuffer->len << 16));
    }
    else
    {
      WRITE_REG(dmatxdesc->DESC1, 0x0);
      /* Set buffer 2 Length */
      MODIFY_REG(dmatxdesc->DESC2, ETH_DMATXNDESCRF_B2L, 0x0U);
    }

    if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_TSO) != 0U)
    {
      /* Set TCP payload length */
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TPL, pTxConfig->PayloadLen);
      /* Set TCP Segmentation Enabled bit */
      SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_TSE);
    }
    else
    {
      /* Set the packet length */
      MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_FL, pTxConfig->Length);

      if(READ_BIT(pTxConfig->Attributes, ETH_TX_PACKETS_FEATURES_CSUM) != 0U)
      {
        /* Checksum Insertion Control */
        MODIFY_REG(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CIC, pTxConfig->ChecksumCtrl);
      }
    }

    bd_count += 1U;

    /* Mark it as NORMAL descriptor */
    CLEAR_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_CTXT);
    
  }

  if(ItMode != ((uint32_t)RESET))
  {
    /* Set Interrupt on completion bit */
    SET_BIT(dmatxdesc->DESC2, ETH_DMATXNDESCRF_IOC);
  }
  else
  {
    /* Clear Interrupt on completion bit */
    CLEAR_BIT(dmatxdesc->DESC2, ETH_DMATXNDESCRF_IOC);
  }

  /* Mark it as LAST descriptor */
  SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_LD);

  __DMB();//Modify!!

  /* Set Own bit For End Desc */
  SET_BIT(dmatxdesc->DESC3, ETH_DMATXNDESCRF_OWN);
  
  /* Save the current packet address to expose it to the application */
  dmatxdesclist->PacketAddress[descidx] = dmatxdesclist->CurrentPacketAddress;

  dmatxdesclist->CurTxDesc = descidx;

  /* disable the interrupt */
  //__disable_irq();
  //What I don't like most is to turn off the interrupt!!
  //dmatxdesclist->BuffersInUse += bd_count + 1U;

  /* Enable interrupts back */
  //__enable_irq();


  /* Return function status */
  return HAL_ETH_ERROR_NONE;
}


HAL_StatusTypeDef HAL_ETH_Transmit(ETH_HandleTypeDef *heth, ETH_TxPacketConfig *pTxConfig, uint32_t Timeout)
{
  uint32_t tickstart;
  const ETH_DMADescTypeDef *dmatxdesc;

  if(pTxConfig == NULL)
  {
    heth->ErrorCode |= HAL_ETH_ERROR_PARAM;
    return HAL_ERROR;
  }

  if(heth->gState == HAL_ETH_STATE_READY)
  {
    /* Config DMA Tx descriptor by Tx Packet info */
    if (ETH_Prepare_Tx_Descriptors(heth, pTxConfig, 0) != HAL_ETH_ERROR_NONE)
    {
      /* Set the ETH error code */
      heth->ErrorCode |= HAL_ETH_ERROR_BUSY;
      return HAL_ERROR;
    }

    dmatxdesc = (ETH_DMADescTypeDef *)(&heth->TxDescList)->TxDesc[heth->TxDescList.CurTxDesc];

    /* Incr current tx desc index */
    INCR_TX_DESC_INDEX(heth->TxDescList.CurTxDesc, 1U);

    /* Start transmission */
    /* issue a poll command to Tx DMA by writing address of next immediate free descriptor */
    //WRITE_REG(heth->Instance->DMACTDTPR, (uint32_t)(heth->TxDescList.TxDesc[heth->TxDescList.CurTxDesc]));
    __DMB();//Modify!!
    WRITE_REG(heth->Instance->DMACTDTPR, ((uint32_t)(heth->Init.TxDesc + (uint32_t)(ETH_TX_DESC_CNT - 1))));

    tickstart = HAL_GetTick();

    /* Wait for data to be transmitted or timeout occurred */
    while((dmatxdesc->DESC3 & ETH_DMATXNDESCWBF_OWN) != (uint32_t)RESET)
    {
      if((heth->Instance->DMACSR & ETH_DMACSR_FBE) != (uint32_t)RESET)
      {
        heth->ErrorCode |= HAL_ETH_ERROR_DMA;
        heth->DMAErrorCode = heth->Instance->DMACSR;
        /* Set ETH HAL State to Ready */
        heth->gState = HAL_ETH_STATE_ERROR;
        /* Return function status */
        return HAL_ERROR;
      }

      /* Check for the Timeout */
      if(Timeout != HAL_MAX_DELAY)
      {
        if(((HAL_GetTick() - tickstart ) > Timeout) || (Timeout == 0U))
        {
          heth->ErrorCode |= HAL_ETH_ERROR_TIMEOUT;
          heth->gState = HAL_ETH_STATE_ERROR;
          return HAL_ERROR;
        }
      }
    }

    /* Return function status */
    return HAL_OK;
  }
  else
  {
    return HAL_ERROR;
  }
}

/**
  * @brief  Sends an Ethernet Packet in interrupt mode.
  * @param  heth: pointer to a ETH_HandleTypeDef structure that contains
  *         the configuration information for ETHERNET module
  * @param  pTxConfig: Hold the configuration of packet to be transmitted
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_ETH_Transmit_IT(ETH_HandleTypeDef *heth, ETH_TxPacketConfig *pTxConfig)
{
  if(pTxConfig == NULL)
  {
    heth->ErrorCode |= HAL_ETH_ERROR_PARAM;
    return HAL_ERROR;
  }

  if(heth->gState == HAL_ETH_STATE_READY)
  {
    /* Config DMA Tx descriptor by Tx Packet info */
    if (ETH_Prepare_Tx_Descriptors(heth, pTxConfig, 1) != HAL_ETH_ERROR_NONE)
    {
      heth->ErrorCode |= HAL_ETH_ERROR_BUSY;
      return HAL_ERROR;
    }

    /* Incr current tx desc index */
    INCR_TX_DESC_INDEX(heth->TxDescList.CurTxDesc, 1U);

    __DMB();//Modify!!

    /* Start transmission */
    /* issue a poll command to Tx DMA by writing address of next immediate free descriptor */
    //WRITE_REG(heth->Instance->DMACTDTPR, (uint32_t)(heth->TxDescList.TxDesc[heth->TxDescList.CurTxDesc]));
    WRITE_REG(heth->Instance->DMACTDTPR, (uint32_t)(heth->TxDescList.TxDesc[ETH_TX_DESC_CNT-1]));

    return HAL_OK;

  }
  else
  {
    return HAL_ERROR;
  }
}

Received modifications

Two receiving related functions have been modified

uint8_t HAL_ETH_IsRxDataAvailable(ETH_HandleTypeDef *heth)
{
  ETH_RxDescListTypeDef *dmarxdesclist = &heth->RxDescList;
  uint32_t descidx = dmarxdesclist->CurRxDesc;
  ETH_DMADescTypeDef *dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
  uint32_t descscancnt = 0;
  uint32_t appdesccnt = 0, firstappdescidx = 0;

  if(dmarxdesclist->AppDescNbr != 0U)
  {
    /* data already received by not yet processed*/
    return 0;
  }

  /* Check if descriptor is not owned by DMA */
  while((READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_OWN) == (uint32_t)RESET) && (descscancnt < (uint32_t)ETH_RX_DESC_CNT))
  {
    descscancnt++;

    /* Check if last descriptor */
    if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_LD) != (uint32_t)RESET)
    {
      /* Increment the number of descriptors to be passed to the application */
      appdesccnt += 1U;

      if(appdesccnt == 1U)
      {
        WRITE_REG(firstappdescidx, descidx);
      }

      /* Increment current rx descriptor index */
      INCR_RX_DESC_INDEX(descidx, 1U);

      /* Check for Context descriptor */
      /* Get current descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];

      if(READ_BIT(dmarxdesc->DESC3,  ETH_DMARXNDESCWBF_OWN)  == (uint32_t)RESET)
      {
        if(READ_BIT(dmarxdesc->DESC3,  ETH_DMARXNDESCWBF_CTXT)  != (uint32_t)RESET)
        {
          /* Increment the number of descriptors to be passed to the application */
          dmarxdesclist->AppContextDesc = 1;
          /* Increment current rx descriptor index */
          INCR_RX_DESC_INDEX(descidx, 1U);
        }
      }
      /* Fill information to Rx descriptors list */
      dmarxdesclist->CurRxDesc = descidx;
      dmarxdesclist->FirstAppDesc = firstappdescidx;
      dmarxdesclist->AppDescNbr = appdesccnt;

      /* Return function status */
      return 1;
    }
    /* Check if first descriptor */
    else if(READ_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCWBF_FD) != (uint32_t)RESET)
    {
      WRITE_REG(firstappdescidx, descidx);
      /* Increment the number of descriptors to be passed to the application */
      appdesccnt = 1U;

      /* Increment current rx descriptor index */
      INCR_RX_DESC_INDEX(descidx, 1U);
      /* Get current descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
    }
    /* It should be an intermediate descriptor */
    else
    {
      /* Increment the number of descriptors to be passed to the application */
      appdesccnt += 1U;

      /* Increment current rx descriptor index */
      INCR_RX_DESC_INDEX(descidx, 1U);
      /* Get current descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
    }
  }

  /* Build Descriptors if an incomplete Packet is received */
  if(appdesccnt > 0U)
  {
    dmarxdesclist->CurRxDesc = descidx;
    dmarxdesclist->FirstAppDesc = firstappdescidx;
    descidx = firstappdescidx;
    dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];

    for(descscancnt = 0; descscancnt < appdesccnt; descscancnt++)
    {
      WRITE_REG(dmarxdesc->DESC0, dmarxdesc->BackupAddr0);
      WRITE_REG(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF1V);

      if (READ_REG(dmarxdesc->BackupAddr1) != ((uint32_t)RESET))
      {
        WRITE_REG(dmarxdesc->DESC2, dmarxdesc->BackupAddr1);
        SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF2V);
      }

      SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN);

      if(dmarxdesclist->ItMode != ((uint32_t)RESET))
      {
        SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_IOC);
      }
      if(descscancnt < (appdesccnt - 1U))
      {
        /* Increment rx descriptor index */
        INCR_RX_DESC_INDEX(descidx, 1U);
        /* Get descriptor address */
        dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descidx];
      }
    }

    /* Set the Tail pointer address to the last rx descriptor hold by the app */
    //WRITE_REG(heth->Instance->DMACRDTPR, (uint32_t)dmarxdesc);
  }

  /* Fill information to Rx descriptors list: No received Packet */
  dmarxdesclist->AppDescNbr = 0U;

  return 0;
}

HAL_StatusTypeDef HAL_ETH_BuildRxDescriptors(ETH_HandleTypeDef *heth)
{
  ETH_RxDescListTypeDef *dmarxdesclist = &heth->RxDescList;
  uint32_t descindex = dmarxdesclist->FirstAppDesc;
  __IO ETH_DMADescTypeDef *dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descindex];
  uint32_t totalappdescnbr = dmarxdesclist->AppDescNbr;
  uint32_t descscan;

  if(dmarxdesclist->AppDescNbr == 0U)
  {
    /* No Rx descriptors to build */
    return HAL_ERROR;
  }

  if(dmarxdesclist->AppContextDesc != 0U)
  {
    /* A context descriptor is available */
    totalappdescnbr += 1U;
  }

  for(descscan =0; descscan < totalappdescnbr; descscan++)
  {
    WRITE_REG(dmarxdesc->DESC0, dmarxdesc->BackupAddr0);
    WRITE_REG(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF1V);

    if (READ_REG(dmarxdesc->BackupAddr1) != 0U)
    {
      WRITE_REG(dmarxdesc->DESC2, dmarxdesc->BackupAddr1);
      SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_BUF2V);
    }

    if(dmarxdesclist->ItMode != 0U){
      SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_IOC);
    }

    __DMB();//Modify!!
    SET_BIT(dmarxdesc->DESC3, ETH_DMARXNDESCRF_OWN);

    if(descscan < (totalappdescnbr - 1U))
    {
      /* Increment rx descriptor index */
      INCR_RX_DESC_INDEX(descindex, 1U);
      /* Get descriptor address */
      dmarxdesc = (ETH_DMADescTypeDef *)dmarxdesclist->RxDesc[descindex];
    }
  }

  __DMB();//Modify!!
  /* Set the Tail pointer address to the last rx descriptor hold by the app */
  //Write_reg (Heth - > instance - > dmacrdtpr, (uint32_t) dmarxdesc); / / modify!!
  WRITE_REG(heth->Instance->DMACRDTPR, ((uint32_t)(heth->Init.RxDesc + (uint32_t)(ETH_RX_DESC_CNT - 1))));

  /* reset the Application desc number */
  WRITE_REG(dmarxdesclist->AppDescNbr, 0);

  /*  reset the application context descriptor */
  WRITE_REG(heth->RxDescList.AppContextDesc, 0);

  return HAL_OK;
}

Other modifications

In fact, the following changes do not matter, because it will be corrected in the later transmission, but OCD can't stand it.

static void ETH_DMATxDescListInit(ETH_HandleTypeDef *heth)
{
  ETH_DMADescTypeDef *dmatxdesc;
  uint32_t i;

  /* Fill each DMATxDesc descriptor with the right values */
  for(i=0; i < (uint32_t)ETH_TX_DESC_CNT; i++)
  {
    dmatxdesc = heth->Init.TxDesc + i;

    WRITE_REG(dmatxdesc->DESC0, 0x0);
    WRITE_REG(dmatxdesc->DESC1, 0x0);
    WRITE_REG(dmatxdesc->DESC2, 0x0);
    WRITE_REG(dmatxdesc->DESC3, 0x0);

    WRITE_REG(heth->TxDescList.TxDesc[i], (uint32_t)dmatxdesc);
  }

  heth->TxDescList.CurTxDesc = 0;

  /* Set Transmit Descriptor Ring Length */
  WRITE_REG(heth->Instance->DMACTDRLR, (ETH_TX_DESC_CNT -1));

  /* Set Transmit Descriptor List Address */
  WRITE_REG(heth->Instance->DMACTDLAR, (uint32_t) heth->Init.TxDesc);

  /* Set Transmit Descriptor Tail pointer *///Modify!!
  WRITE_REG(heth->Instance->DMACTDTPR, ((uint32_t)(heth->Init.TxDesc + (uint32_t)(ETH_TX_DESC_CNT - 1))));//Modify!!
}

/**
  * @brief  Initializes the DMA Rx descriptors in chain mode.
  *         called by HAL_ETH_Init() API.
  * @param  heth: pointer to a ETH_HandleTypeDef structure that contains
  *         the configuration information for ETHERNET module
  * @retval None
  */
static void ETH_DMARxDescListInit(ETH_HandleTypeDef *heth)
{
  ETH_DMADescTypeDef *dmarxdesc;
  uint32_t i;

  for(i = 0; i < (uint32_t)ETH_RX_DESC_CNT; i++)
  {
    dmarxdesc =  heth->Init.RxDesc + i;

    WRITE_REG(dmarxdesc->DESC0, 0x0);
    WRITE_REG(dmarxdesc->DESC1, 0x0);
    WRITE_REG(dmarxdesc->DESC2, 0x0);
    WRITE_REG(dmarxdesc->DESC3, 0x0);
    WRITE_REG(dmarxdesc->BackupAddr0, 0x0);
    WRITE_REG(dmarxdesc->BackupAddr1, 0x0);

    /* Set Rx descritors addresses */
    WRITE_REG(heth->RxDescList.RxDesc[i], (uint32_t)dmarxdesc);
  }

  WRITE_REG(heth->RxDescList.CurRxDesc, 0);
  WRITE_REG(heth->RxDescList.FirstAppDesc, 0);
  WRITE_REG(heth->RxDescList.AppDescNbr, 0);
  WRITE_REG(heth->RxDescList.ItMode, 0);
  WRITE_REG(heth->RxDescList.AppContextDesc, 0);

  /* Set Receive Descriptor Ring Length */
  WRITE_REG(heth->Instance->DMACRDRLR, ((uint32_t)(ETH_RX_DESC_CNT - 1)));

  /* Set Receive Descriptor List Address */
  WRITE_REG(heth->Instance->DMACRDLAR, (uint32_t) heth->Init.RxDesc);

  /* Set Receive Descriptor Tail pointer Address *///Modify!!
  WRITE_REG(heth->Instance->DMACRDTPR, ((uint32_t)(heth->Init.RxDesc + (uint32_t)(ETH_RX_DESC_CNT - 1))));//Modify!!
}

Tags: Single-Chip Microcomputer stm32 ARM lwip

Posted on Sun, 28 Nov 2021 13:26:43 -0500 by harshilshah