DMA fails to transmit Ethernet frame with STM32F765 board.
I am trying to implement Ethernet capability using the LwIP middleware on a STM32F765 board with a LAN8742A PHY.
I have observed that the firmware/hardware functions properly when receiving an ARP request.
However, when the low_level_output routine (in ethernetif.c) calls the HAL_ETH_TransmitFrame routine (in stm32f7xx_hal_eth.c) to transmit the ARP response frame, the ARP response frame is never sent. (I am using a packet sniffer to monitor the traffic on the LAN.)
After almost 50 hours of debugging, I have determined that, for some reason, the transmission to the MAC is not being completed as evidenced by the beginning and ending status.
The following is the source code for the HAL_ETH_TransmitFrame routine. I added two blocks of code which are bracketed by //$$$$ ... $$$ lines. The first block reads the initial status (from the DMASR), resets the status bits and then reads the status bits after reset. The contents of the status register bits in the first block are shown in the listing below. The second block is a wait loop at the end of the routine which polls the status register until the Transmission Complete (TS) bit is set. The contents of the status register bits in the second block are shown in the listing below.
When debugging, I observed that the TS bit is never set causing execution to not progress beyond the wait loop in the second block of code (which was intentional). The only change in the status between the first and second block is that the ETS bit (10) in the DMA status register is set in the second block. This indicates that the Ethernet frame data was transferred to the FIFO in the MAC.
I have seen a number of posts that mention problems like this but I have not found any clear suggestions as to how to resolve this issue!!
I am using the latest versions of the STM32CubeMX IDE and the LwIP middleware. All of the clocks, including the Ethernet PTP, are running at 100 MHz except for the APB1 Peripheral Clock which is running at 50 MHz.
Any help would be greatly appreciated!!
/**
* @brief Sends an Ethernet frame.
* @param heth pointer to a ETH_HandleTypeDef structure that contains
* the configuration information for ETHERNET module
* @param FrameLength Amount of data to be sent
* @retval HAL status
*/
HAL_StatusTypeDef HAL_ETH_TransmitFrame(ETH_HandleTypeDef *heth, uint32_t FrameLength)
{
uint32_t bufcount = 0, size = 0, i = 0;
/* Process Locked */
__HAL_LOCK(heth);
/* Set the ETH peripheral state to BUSY */
heth->State = HAL_ETH_STATE_BUSY;
if (FrameLength == 0)
{
/* Set ETH HAL state to READY */
heth->State = HAL_ETH_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(heth);
return HAL_ERROR;
}
/* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
if(((heth->TxDesc)->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
{
/* OWN bit set */
heth->State = HAL_ETH_STATE_BUSY_TX;
/* Process Unlocked */
__HAL_UNLOCK(heth);
return HAL_ERROR;
}
/* Get the number of needed Tx buffers for the current frame */
if (FrameLength > ETH_TX_BUF_SIZE)
{
bufcount = FrameLength/ETH_TX_BUF_SIZE;
if (FrameLength % ETH_TX_BUF_SIZE)
{
bufcount++;
}
}
else
{
bufcount = 1;
}
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
// Clear the status:
uint32_t dma_status=
(heth->Instance)->DMASR; // get the initial DMA status
(dma_status before reset is: 0x660044 )
(heth->Instance)->DMASR= // reset all of the status bits
(0xffff ^ ETH_DMASR_TBUS); // except TBUF
dma_status=(heth->Instance)->DMASR; // check the status after reset
(dma_status after reset is: 0x6800C4 )
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
if (bufcount == 1)
{
/* Set LAST and FIRST segment */
heth->TxDesc->Status |=ETH_DMATXDESC_FS|ETH_DMATXDESC_LS;
/* Set frame size */
heth->TxDesc->ControlBufferSize = (FrameLength & ETH_DMATXDESC_TBS1);
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
heth->TxDesc->Status |= ETH_DMATXDESC_OWN;
/* Point to next descriptor */
heth->TxDesc= (ETH_DMADescTypeDef *)(heth->TxDesc->Buffer2NextDescAddr);
}
else
{
for (i=0; i< bufcount; i++)
{
/* Clear FIRST and LAST segment bits */
heth->TxDesc->Status &= ~(ETH_DMATXDESC_FS | ETH_DMATXDESC_LS);
if (i == 0)
{
/* Setting the first segment bit */
heth->TxDesc->Status |= ETH_DMATXDESC_FS;
}
/* Program size */
heth->TxDesc->ControlBufferSize = (ETH_TX_BUF_SIZE & ETH_DMATXDESC_TBS1);
if (i == (bufcount-1))
{
/* Setting the last segment bit */
heth->TxDesc->Status |= ETH_DMATXDESC_LS;
size = FrameLength - (bufcount-1)*ETH_TX_BUF_SIZE;
heth->TxDesc->ControlBufferSize = (size & ETH_DMATXDESC_TBS1);
}
/* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
heth->TxDesc->Status |= ETH_DMATXDESC_OWN;
/* point to next descriptor */
heth->TxDesc = (ETH_DMADescTypeDef *)(heth->TxDesc->Buffer2NextDescAddr);
}
}
/* When Tx Buffer unavailable flag is set: clear it and resume transmission */
if (((heth->Instance)->DMASR & ETH_DMASR_TBUS) != (uint32_t)RESET)
{
/* Clear TBUS ETHERNET DMA flag */
(heth->Instance)->DMASR = ETH_DMASR_TBUS;
/* Resume DMA transmission*/
(heth->Instance)->DMATPDR = 0;
}
/* Set ETH HAL State to Ready */
heth->State = HAL_ETH_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(heth);
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
// Wait for transmission to complete:
while (1) // wait for the TS bit to be set
{ // indicating transmission done
dma_status=
(heth->Instance)->DMASR;
(dma_status here is: 0x6804C4 )
if (dma_status &
ETH_DMASR_TS) break;
}
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
/* Return function status */
return HAL_OK;
}
