From 412f4dc21b86472d04da6b06c682f93a1a549b19 Mon Sep 17 00:00:00 2001 From: Amomum Date: Tue, 20 Mar 2018 15:56:52 +0300 Subject: [PATCH] ETH_SendFrame works in linear mode. Auto mode breaks after a several hundred packets, FIFO is still broken completely. Suffering from code style was unbearable so I completely refactored it. Linear mode was tested (with different delimiter values) with LwIP and ping -f --- no packet loss. refs #41 --- .../inc/MDR32F9Qx_eth.h | 13 +- .../src/MDR32F9Qx_eth.c | 197 +++++++++++------- 2 files changed, 133 insertions(+), 77 deletions(-) diff --git a/MDR32F9Qx_StdPeriph_Driver/inc/MDR32F9Qx_eth.h b/MDR32F9Qx_StdPeriph_Driver/inc/MDR32F9Qx_eth.h index f6b566f..6e58867 100644 --- a/MDR32F9Qx_StdPeriph_Driver/inc/MDR32F9Qx_eth.h +++ b/MDR32F9Qx_StdPeriph_Driver/inc/MDR32F9Qx_eth.h @@ -68,7 +68,7 @@ typedef struct { * @brief MAC General Config */ - uint32_t ETH_Delimiter; /*!< Defines the boundaries of the transmitter and receiver buffers. + uint32_t ETH_Delimiter; /*!< Defines the boundaries of the transmitter and receiver buffers. This parameter can be a value from 0 to 0x1FFF. */ uint32_t ETH_DBG_Mode; /*!< Select the mode of operation in debug mode. This parameter can be a value of @ref ETH_DBG_MODE. */ @@ -204,6 +204,15 @@ typedef union { * @{ */ + +/** @defgroup ETH_Buf_Size + * @{ + */ + +#define ETH_BUFFER_SIZE_IN_BYTES ((uint32_t)0x2000) + +/** @} */ /* End of group ETH_Buf_Size */ + /** @defgroup ETH_MODE ETH_MODE * @{ */ @@ -552,7 +561,7 @@ FlagStatus ETH_GetPHYStatus(MDR_ETHERNET_TypeDef * ETHERNETx, uint16_t ETH_PHY_F uint16_t ETH_ReadPHYRegister(MDR_ETHERNET_TypeDef * ETHERNETx, uint16_t PHYAddress, uint16_t PHYReg); uint32_t ETH_WritePHYRegister(MDR_ETHERNET_TypeDef * ETHERNETx, uint16_t PHYAddress, uint16_t PHYReg, uint16_t PHYValue); uint32_t ETH_ReceivedFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_InputBuffer); -void ETH_SendFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_OututBuffer, uint32_t BufLen); +uint32_t ETH_SendFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_OututBuffer, uint32_t BufLen); void ETH_DMAPrepare(void); void ETH_DMAFrameRx(uint32_t * DstBuf, uint32_t BufferSize, uint32_t * SrcBuf); void ETH_DMAFrameTx(uint32_t * DstBuf, uint32_t BufferSize, uint32_t * SrcBuf); diff --git a/MDR32F9Qx_StdPeriph_Driver/src/MDR32F9Qx_eth.c b/MDR32F9Qx_StdPeriph_Driver/src/MDR32F9Qx_eth.c index 88bd7e3..456a93c 100644 --- a/MDR32F9Qx_StdPeriph_Driver/src/MDR32F9Qx_eth.c +++ b/MDR32F9Qx_StdPeriph_Driver/src/MDR32F9Qx_eth.c @@ -63,7 +63,7 @@ extern DMA_CtrlDataTypeDef DMA_ControlTable[DMA_Channels_Number * (1 + DMA_Alter #define IS_ETH_RETRY_COUNTER(COUNTER) (COUNTER <= 0x0F) #define IS_ETH_DELIMITER(DELIMITER) ((DELIMITER >= 0x5EA) && (DELIMITER <= 0x1A16)) -#define ETH_BUFFER_SIZE ((uint32_t)0x2000) + /** @} */ /* End of group ETH_Private_Defines */ @@ -261,7 +261,7 @@ void ETH_DeInit(MDR_ETHERNET_TypeDef * ETHERNETx ) /* PHY reset */ ETH_PHY_Reset(ETHERNETx); - ETHERNETx->ETH_Delimiter = 0x0800; + ETHERNETx->ETH_Delimiter = 0x0800; ETHERNETx->ETH_MAC_T = 0x78AB; ETHERNETx->ETH_MAC_M = 0x3456; ETHERNETx->ETH_MAC_H = 0x0012; @@ -299,7 +299,7 @@ void ETH_StructInit(ETH_InitTypeDef * ETH_InitStruct) ETH_InitStruct->ETH_PHY_Interface = ETH_PHY_INTERFACE_ETHERNET_802_3; /* General config*/ - ETH_InitStruct->ETH_Delimiter = 0x0800; + ETH_InitStruct->ETH_Delimiter = 0x0800; /* Set the DBG Mode */ ETH_InitStruct->ETH_DBG_Mode = ETH_DBG_MODE_FREE_RUN; /* Enable automatically change the transmitter FIFO pointers in DBG Mode. */ @@ -414,7 +414,7 @@ void ETH_Init(MDR_ETHERNET_TypeDef * ETHERNETx, ETH_InitTypeDef * ETH_InitStruct /* Check the parameters */ assert_param(IS_ETH_ALL_PERIPH(ETHERNETx)); - assert_param(IS_ETH_DELIMITER(ETH_InitStruct->ETH_Delimiter)); + assert_param(IS_ETH_DELIMITER(ETH_InitStruct->ETH_Delimiter)); assert_param(IS_ETH_PHY_ADDRESS(ETH_InitStruct->ETH_PHY_Address)); assert_param(IS_ETH_PHY_MODE(ETH_InitStruct->ETH_PHY_Mode)); assert_param(IS_ETH_DBG_MODE(ETH_InitStruct->ETH_DBG_Mode)); @@ -454,7 +454,7 @@ void ETH_Init(MDR_ETHERNET_TypeDef * ETHERNETx, ETH_InitTypeDef * ETH_InitStruct assert_param(IS_FUNCTIONAL_STATE(ETH_InitStruct->ETH_Source_Addr_HASH_Filter)); /* Set the buffer size of transmitter and receiver */ - ETHERNETx->ETH_Delimiter = ETH_InitStruct->ETH_Delimiter; + ETHERNETx->ETH_Delimiter = ETH_InitStruct->ETH_Delimiter; /* Config the PHY control register */ tmpreg_PHY_Control = (ETH_InitStruct->ETH_PHY_Address << ETH_PHY_CONTROL_PHYADD_Pos) | (ETH_InitStruct->ETH_PHY_Mode) | (ETH_InitStruct->ETH_PHY_Interface); @@ -975,7 +975,7 @@ uint32_t ETH_ReceivedFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_Inpu ETH_StatusPacketReceptionStruct.Status = (uint32_t)*ptr_InputFrame++; PacketLength = (ETH_StatusPacketReceptionStruct.Fields.Length + 3)/4; /* Read the input frame */ - EthReceiverFreeBufferSize = (uint32_t) (ETHERNETx->ETH_Delimiter - Rhead) - PacketLength*4; + EthReceiverFreeBufferSize = (uint32_t) (ETHERNETx->ETH_Delimiter - Rhead) - PacketLength*4; if(EthReceiverFreeBufferSize > 0){ /* Read the input frame */ for(i = 0; i < PacketLength; i++){ @@ -1009,7 +1009,7 @@ uint32_t ETH_ReceivedFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_Inpu /* Set the Length of receiving paket */ PacketLength = ((ETH_StatusPacketReceptionStruct.Fields.Length & 0x0003) != 0) + ETH_StatusPacketReceptionStruct.Fields.Length/4; /* Get the size of recever buffer */ - EthReceiverFreeBufferSize = (uint32_t) (ETHERNETx->ETH_Delimiter - Rhead) - PacketLength*4; + EthReceiverFreeBufferSize = (uint32_t) (ETHERNETx->ETH_Delimiter - Rhead) - PacketLength*4; if(EthReceiverFreeBufferSize > 0){ /* Read the input frame */ for(i = 0; i < PacketLength; i++){ @@ -1054,84 +1054,131 @@ uint32_t ETH_ReceivedFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_Inpu * MDR_ETHERNET1, MDR_ETHERNET2 for MDR1986VE3 and * MDR_ETHERNET1 for MDR1986VE1T. * @param ptr_OututBuffer: pointer to the sending frame. - * @param BufLen: the size of the sending frmae. - * @retval None + * @param BufLen: the size of the sending frame in bytes. + * @retval ETH_ERROR if there is an error (not enough space in output buffer) + * ETH_SUCCESS otherwise */ -void ETH_SendFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_OutputBuffer, uint32_t BufLen) + +uint32_t ETH_SendFrame(MDR_ETHERNET_TypeDef * ETHERNETx, uint32_t * ptr_BufferToSend, uint32_t BufLenInBytes) { - uint32_t BufferMode, i, Xtail, tmp; - uint32_t * ptr_OutputFrame; - int32_t EthReceiverFreeBufferSize; + uint32_t BufferMode; + uint32_t Xtail_InBytes; + uint32_t Xhead_InBytes; + uint32_t * ptr_PeriphDataBuffer; + uint32_t WordsAvailable[2]; + + /* Since output buffer is uint32_t, it should be safe to round up its length in bytes to word boundary */ + /* We have to do it because ethernet peripheral buffer only supports word access */ + const uint32_t BufLenInWords = (BufLenInBytes + 3)/4; + + /* Buffer length in bytes, rounded up to word boundary, plus word for length at the beginning */ + /* plus reserved word at the end */ + const uint32_t OutputLenInWords = BufLenInWords + 2; /* Check the parameters */ assert_param(IS_ETH_ALL_PERIPH(ETHERNETx)); + Xtail_InBytes = ETHERNETx->ETH_X_Tail; + Xhead_InBytes = ETHERNETx->ETH_X_Head; + /* Read the buffer mode */ BufferMode = (ETHERNETx->ETH_G_CFGl & ETH_G_CFGl_BUFF_MODE_Msk); - /* Send packet */ - Xtail = ETHERNETx->ETH_X_Tail; - switch (BufferMode){ - case ETH_BUFFER_MODE_LINEAR: - /* Set pointer to output buffer */ - ptr_OutputFrame = (uint32_t *)((((uint32_t)ETHERNETx) + 0x08000000) + Xtail); - /* Send frame */ - EthReceiverFreeBufferSize = (ETH_BUFFER_SIZE - Xtail) / 4; - /* Put size of the frame first*/ - *ptr_OutputFrame++ = BufLen; - EthReceiverFreeBufferSize--; - if(((BufLen + 3) / 4 + 2) < (uint32_t)EthReceiverFreeBufferSize){ - for( i = 0; i < (BufLen + 3)/4 + 2; i++ ){ - *ptr_OutputFrame++ = ptr_OutputBuffer[i]; - } - } - else{ - for( i = 0; i < (uint32_t)EthReceiverFreeBufferSize; i++ ){ - *ptr_OutputFrame++ = ptr_OutputBuffer[i]; - } - tmp = i; - ptr_OutputFrame = (uint32_t *)((((uint32_t)ETHERNETx) + 0x08000000) + ETHERNETx->ETH_Delimiter); - for(i = 0; i < (((BufLen + 3)/4 + 2) - EthReceiverFreeBufferSize); i++){ - *ptr_OutputFrame++ = ptr_OutputBuffer[i+tmp]; - } - } - Xtail = (uint32_t)ptr_OutputFrame&0x1FFC; - if(Xtail >= ETH_BUFFER_SIZE) - Xtail = ETHERNETx->ETH_Delimiter; - /* Write the new value of the ETH_X_Tail register */ - ETHERNETx->ETH_X_Tail = Xtail; - break; - case ETH_BUFFER_MODE_AUTOMATIC_CHANGE_POINTERS: - /* Set pointer to output buffer */ - ptr_OutputFrame = (uint32_t *)((((uint32_t)ETHERNETx) + 0x08000000) + Xtail); - /* Send frame */ - EthReceiverFreeBufferSize = (ETH_BUFFER_SIZE - Xtail) / 4; - /* Put size of the frame first*/ - *ptr_OutputFrame++ = BufLen; - EthReceiverFreeBufferSize--; - if(((BufLen + 3) / 4 + 2) < (uint32_t)EthReceiverFreeBufferSize){ - for( i = 0; i < (BufLen + 3)/4 + 2; i++ ){ - *ptr_OutputFrame++ = ptr_OutputBuffer[i]; - } - } - else{ - for( i = 0; i < (uint32_t)EthReceiverFreeBufferSize; i++ ){ - *ptr_OutputFrame++ = ptr_OutputBuffer[i]; - } - tmp = i; - ptr_OutputFrame = (uint32_t *)((((uint32_t)ETHERNETx) + 0x08000000) + ETHERNETx->ETH_Delimiter); - for(i = 0; i < (((BufLen + 3)/4 + 2) - EthReceiverFreeBufferSize); i++){ - *ptr_OutputFrame++ = ptr_OutputBuffer[i+tmp]; - } - } - break; - case ETH_BUFFER_MODE_FIFO: - /* Set the pointer to input frame */ - ptr_OutputFrame = (uint32_t *) ((uint32_t)ETHERNETx + 0x08000004); - /* Send frame */ - ETH_DMAFrameTx(ptr_OutputFrame, ((BufLen+3)/4 + 2), ptr_OutputBuffer); - break; + if( BufferMode == ETH_BUFFER_MODE_FIFO ){ + /* Set the pointer to input frame */ + ptr_PeriphDataBuffer = (uint32_t *) (MDR_ETHERNET1_BUF_BASE + 4); + /* Send frame */ + ETH_DMAFrameTx(ptr_PeriphDataBuffer, OutputLenInWords, ptr_BufferToSend); + + return ETH_SUCCESS; } + + /* Set pointer to output buffer */ + ptr_PeriphDataBuffer = (uint32_t *)(MDR_ETHERNET1_BUF_BASE + Xtail_InBytes); + + /* Calculate available space, it can be split in two pieces */ + if( Xhead_InBytes > Xtail_InBytes ) + { + WordsAvailable[0] = (Xhead_InBytes - Xtail_InBytes) / 4; + WordsAvailable[1] = 0; + } + else + { + WordsAvailable[0] = (ETH_BUFFER_SIZE_IN_BYTES - Xtail_InBytes) / 4; + WordsAvailable[1] = (Xhead_InBytes - ETHERNETx->ETH_Delimiter) / 4; + } + + /* If there is not enough space in the peripheral buffer - return error */ + if( OutputLenInWords > WordsAvailable[0] + WordsAvailable[1] ) + { + return ETH_ERROR; + } + + /* Put size of the frame in bytes first, if there is a space for it */ + if( WordsAvailable[0] >= 1){ + + *ptr_PeriphDataBuffer = BufLenInBytes; + ptr_PeriphDataBuffer++; + WordsAvailable[0]--; + Xtail_InBytes += 4; + } + else{ + ptr_PeriphDataBuffer = (uint32_t *)(MDR_ETHERNET1_BUF_BASE + ETHERNETx->ETH_Delimiter); + Xtail_InBytes = ETHERNETx->ETH_Delimiter; + + *ptr_PeriphDataBuffer = BufLenInBytes; + ptr_PeriphDataBuffer++; + WordsAvailable[1]--; + Xtail_InBytes += 4; + } + + /* Put frame body to the peripheral buffer plus reserved word for tx status */ + /* We can't use memcpy because we have to ensure word access to peripheral buffer */ + if( BufLenInWords + 1 <= WordsAvailable[0]){ + + uint32_t i; + for( i=0; i < BufLenInWords; i++ ) + { + ptr_PeriphDataBuffer[i] = ptr_BufferToSend[i]; + } + /* reserve word for tx status, required in auto mode*/ + ptr_PeriphDataBuffer[i] = 0; + + Xtail_InBytes += (BufLenInWords + 1)*4; + } + else{ + uint32_t i; + /* Put what we can fit into first piece */ + for( i=0; i < WordsAvailable[0]; i++ ) + { + ptr_PeriphDataBuffer[i] = ptr_BufferToSend[i]; + } + + ptr_PeriphDataBuffer = (uint32_t *)(MDR_ETHERNET1_BUF_BASE + ETHERNETx->ETH_Delimiter); + ptr_BufferToSend += WordsAvailable[0]; + + /* Put the rest of it in the second piece. It should be big enough, we checked it before */ + for( i=0; i < BufLenInWords - WordsAvailable[0]; i++ ) + { + ptr_PeriphDataBuffer[i] = ptr_BufferToSend[i]; + } + + Xtail_InBytes = ETHERNETx->ETH_Delimiter + (BufLenInWords - WordsAvailable[0]) * 4; + + /* reserve word for tx status, required in auto mode*/ + ptr_PeriphDataBuffer[i] = 0; + Xtail_InBytes += 4; + } + + if( BufferMode == ETH_BUFFER_MODE_LINEAR ){ + if(Xtail_InBytes >= ETH_BUFFER_SIZE_IN_BYTES) { + Xtail_InBytes -= (ETH_BUFFER_SIZE_IN_BYTES - ETHERNETx->ETH_Delimiter); + } + /* Write the new value of the ETH_X_Tail register */ + ETHERNETx->ETH_X_Tail = Xtail_InBytes; + } + + return ETH_SUCCESS; } /**