/* | |
* FreeRTOS+TCP Labs Build 200417 (C) 2016 Real Time Engineers ltd. | |
* Authors include Hein Tibosch and Richard Barry | |
* | |
******************************************************************************* | |
***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE *** | |
*** *** | |
*** *** | |
*** FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP *** | |
*** demos have a dependency on FreeRTOS+FAT, which is only in the Labs *** | |
*** download): *** | |
*** *** | |
*** FreeRTOS+TCP is functional and has been used in commercial products *** | |
*** for some time. Be aware however that we are still refining its *** | |
*** design, the source code does not yet quite conform to the strict *** | |
*** coding and style standards mandated by Real Time Engineers ltd., and *** | |
*** the documentation and testing is not necessarily complete. *** | |
*** *** | |
*** PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE *** | |
*** URL: http://www.FreeRTOS.org/contact Active early adopters may, at *** | |
*** the sole discretion of Real Time Engineers Ltd., be offered versions *** | |
*** under a license other than that described below. *** | |
*** *** | |
*** *** | |
***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE *** | |
******************************************************************************* | |
* | |
* FreeRTOS+TCP can be used under two different free open source licenses. The | |
* license that applies is dependent on the processor on which FreeRTOS+TCP is | |
* executed, as follows: | |
* | |
* If FreeRTOS+TCP is executed on one of the processors listed under the Special | |
* License Arrangements heading of the FreeRTOS+TCP license information web | |
* page, then it can be used under the terms of the FreeRTOS Open Source | |
* License. If FreeRTOS+TCP is used on any other processor, then it can be used | |
* under the terms of the GNU General Public License V2. Links to the relevant | |
* licenses follow: | |
* | |
* The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license | |
* The FreeRTOS Open Source License: http://www.FreeRTOS.org/license | |
* The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt | |
* | |
* FreeRTOS+TCP is distributed in the hope that it will be useful. You cannot | |
* use FreeRTOS+TCP unless you agree that you use the software 'as is'. | |
* FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied | |
* warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
* PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they | |
* implied, expressed, or statutory. | |
* | |
* 1 tab == 4 spaces! | |
* | |
* http://www.FreeRTOS.org | |
* http://www.FreeRTOS.org/plus | |
* http://www.FreeRTOS.org/labs | |
* | |
*/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "queue.h" | |
#include "semphr.h" | |
/* FreeRTOS+TCP includes. */ | |
#include "FreeRTOS_IP.h" | |
#include "FreeRTOS_Sockets.h" | |
#include "FreeRTOS_IP_Private.h" | |
#include "NetworkBufferManagement.h" | |
#include "NetworkInterface.h" | |
/* Xilinx library files. */ | |
#include <xemacps.h> | |
#include "Zynq/x_topology.h" | |
#include "Zynq/x_emacpsif.h" | |
#include "Zynq/x_emacpsif_hw.h" | |
/* Provided memory configured as uncached. */ | |
#include "uncached_memory.h" | |
#ifndef BMSR_LINK_STATUS | |
#define BMSR_LINK_STATUS 0x0004UL | |
#endif | |
#ifndef PHY_LS_HIGH_CHECK_TIME_MS | |
/* Check if the LinkSStatus in the PHY is still high after 15 seconds of not | |
receiving packets. */ | |
#define PHY_LS_HIGH_CHECK_TIME_MS 15000 | |
#endif | |
#ifndef PHY_LS_LOW_CHECK_TIME_MS | |
/* Check if the LinkSStatus in the PHY is still low every second. */ | |
#define PHY_LS_LOW_CHECK_TIME_MS 1000 | |
#endif | |
/* The size of each buffer when BufferAllocation_1 is used: | |
http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */ | |
#define niBUFFER_1_PACKET_SIZE 1536 | |
/* Naming and numbering of PHY registers. */ | |
#define PHY_REG_01_BMSR 0x01 /* Basic mode status register */ | |
#ifndef iptraceEMAC_TASK_STARTING | |
#define iptraceEMAC_TASK_STARTING() do { } while( 0 ) | |
#endif | |
/* Default the size of the stack used by the EMAC deferred handler task to twice | |
the size of the stack used by the idle task - but allow this to be overridden in | |
FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */ | |
#ifndef configEMAC_TASK_STACK_SIZE | |
#define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE ) | |
#endif | |
/*-----------------------------------------------------------*/ | |
/* | |
* Look for the link to be up every few milliseconds until either xMaxTime time | |
* has passed or a link is found. | |
*/ | |
static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ); | |
/* | |
* A deferred interrupt handler for all MAC/DMA interrupt sources. | |
*/ | |
static void prvEMACHandlerTask( void *pvParameters ); | |
/*-----------------------------------------------------------*/ | |
/* EMAC data/descriptions. */ | |
static xemacpsif_s xEMACpsif; | |
struct xtopology_t xXTopology = | |
{ | |
.emac_baseaddr = XPAR_PS7_ETHERNET_0_BASEADDR, | |
.emac_type = xemac_type_emacps, | |
.intc_baseaddr = 0x0, | |
.intc_emac_intr = 0x0, | |
.scugic_baseaddr = XPAR_PS7_SCUGIC_0_BASEADDR, | |
.scugic_emac_intr = 0x36, | |
}; | |
XEmacPs_Config mac_config = | |
{ | |
.DeviceId = XPAR_PS7_ETHERNET_0_DEVICE_ID, /**< Unique ID of device */ | |
.BaseAddress = XPAR_PS7_ETHERNET_0_BASEADDR /**< Physical base address of IPIF registers */ | |
}; | |
extern int phy_detected; | |
/* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ | |
static uint32_t ulPHYLinkStatus = 0; | |
#if( ipconfigUSE_LLMNR == 1 ) | |
static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC }; | |
#endif | |
/* ucMACAddress as it appears in main.c */ | |
extern const uint8_t ucMACAddress[ 6 ]; | |
/* Holds the handle of the task used as a deferred interrupt processor. The | |
handle is used so direct notifications can be sent to the task for all EMAC/DMA | |
related interrupts. */ | |
TaskHandle_t xEMACTaskHandle = NULL; | |
/*-----------------------------------------------------------*/ | |
BaseType_t xNetworkInterfaceInitialise( void ) | |
{ | |
uint32_t ulLinkSpeed, ulDMAReg; | |
BaseType_t xStatus, xLinkStatus; | |
XEmacPs *pxEMAC_PS; | |
const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 7000UL ), xWaitRelinkDelay = pdMS_TO_TICKS( 1000UL ); | |
/* Guard against the init function being called more than once. */ | |
if( xEMACTaskHandle == NULL ) | |
{ | |
pxEMAC_PS = &( xEMACpsif.emacps ); | |
memset( &xEMACpsif, '\0', sizeof( xEMACpsif ) ); | |
xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &mac_config, mac_config.BaseAddress); | |
if( xStatus != XST_SUCCESS ) | |
{ | |
FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) ); | |
} | |
/* Initialize the mac and set the MAC address. */ | |
XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) ucMACAddress, 1 ); | |
#if( ipconfigUSE_LLMNR == 1 ) | |
{ | |
/* Also add LLMNR multicast MAC address. */ | |
XEmacPs_SetMacAddress( pxEMAC_PS, ( void * )xLLMNR_MACAddress, 2 ); | |
} | |
#endif /* ipconfigUSE_LLMNR == 1 */ | |
XEmacPs_SetMdioDivisor( pxEMAC_PS, MDC_DIV_224 ); | |
ulLinkSpeed = Phy_Setup( pxEMAC_PS ); | |
XEmacPs_SetOperatingSpeed( pxEMAC_PS, ulLinkSpeed); | |
/* Setting the operating speed of the MAC needs a delay. */ | |
vTaskDelay( pdMS_TO_TICKS( 25UL ) ); | |
ulDMAReg = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET); | |
/* DISC_WHEN_NO_AHB: when set, the GEM DMA will automatically discard receive | |
packets from the receiver packet buffer memory when no AHB resource is available. */ | |
XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET, | |
ulDMAReg | XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK); | |
setup_isr( &xEMACpsif ); | |
init_dma( &xEMACpsif ); | |
start_emacps( &xEMACpsif ); | |
prvGMACWaitLS( xWaitLinkDelay ); | |
/* The deferred interrupt handler task is created at the highest | |
possible priority to ensure the interrupt handler can return directly | |
to it. The task's handle is stored in xEMACTaskHandle so interrupts can | |
notify the task when there is something to process. */ | |
xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle ); | |
} | |
else | |
{ | |
/* Initialisation was already performed, just wait for the link. */ | |
prvGMACWaitLS( xWaitRelinkDelay ); | |
} | |
/* Only return pdTRUE when the Link Status of the PHY is high, otherwise the | |
DHCP process and all other communication will fail. */ | |
xLinkStatus = xGetPhyLinkStatus(); | |
return ( xLinkStatus != pdFALSE ); | |
} | |
/*-----------------------------------------------------------*/ | |
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxBuffer, BaseType_t bReleaseAfterSend ) | |
{ | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) | |
{ | |
iptraceNETWORK_INTERFACE_TRANSMIT(); | |
emacps_send_message( &xEMACpsif, pxBuffer, bReleaseAfterSend ); | |
} | |
else if( bReleaseAfterSend != pdFALSE ) | |
{ | |
/* No link. */ | |
vReleaseNetworkBufferAndDescriptor( pxBuffer ); | |
} | |
return pdTRUE; | |
} | |
/*-----------------------------------------------------------*/ | |
static inline unsigned long ulReadMDIO( unsigned ulRegister ) | |
{ | |
uint16_t usValue; | |
XEmacPs_PhyRead( &( xEMACpsif.emacps ), phy_detected, ulRegister, &usValue ); | |
return usValue; | |
} | |
/*-----------------------------------------------------------*/ | |
static BaseType_t prvGMACWaitLS( TickType_t xMaxTime ) | |
{ | |
TickType_t xStartTime, xEndTime; | |
const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL ); | |
BaseType_t xReturn; | |
xStartTime = xTaskGetTickCount(); | |
for( ;; ) | |
{ | |
xEndTime = xTaskGetTickCount(); | |
if( xEndTime - xStartTime > xMaxTime ) | |
{ | |
xReturn = pdFALSE; | |
break; | |
} | |
ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR ); | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) | |
{ | |
xReturn = pdTRUE; | |
break; | |
} | |
vTaskDelay( xShortDelay ); | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) | |
{ | |
static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) ); | |
uint8_t *ucRAMBuffer = ucNetworkPackets; | |
uint32_t ul; | |
for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) | |
{ | |
pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; | |
*( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) ); | |
ucRAMBuffer += niBUFFER_1_PACKET_SIZE; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
BaseType_t xGetPhyLinkStatus( void ) | |
{ | |
BaseType_t xReturn; | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 ) | |
{ | |
xReturn = pdFALSE; | |
} | |
else | |
{ | |
xReturn = pdTRUE; | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvEMACHandlerTask( void *pvParameters ) | |
{ | |
TimeOut_t xPhyTime; | |
TickType_t xPhyRemTime; | |
UBaseType_t uxLastMinBufferCount = 0; | |
UBaseType_t uxCurrentCount; | |
BaseType_t xResult = 0; | |
uint32_t xStatus; | |
const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL ); | |
/* Remove compiler warnings about unused parameters. */ | |
( void ) pvParameters; | |
/* A possibility to set some additional task properties like calling | |
portTASK_USES_FLOATING_POINT() */ | |
iptraceEMAC_TASK_STARTING(); | |
vTaskSetTimeOutState( &xPhyTime ); | |
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); | |
for( ;; ) | |
{ | |
uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); | |
if( uxLastMinBufferCount != uxCurrentCount ) | |
{ | |
/* The logging produced below may be helpful | |
while tuning +TCP: see how many buffers are in use. */ | |
uxLastMinBufferCount = uxCurrentCount; | |
FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", | |
uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); | |
} | |
#if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) | |
{ | |
static UBaseType_t uxLastMinQueueSpace = 0; | |
uxCurrentCount = uxGetMinimumIPQueueSpace(); | |
if( uxLastMinQueueSpace != uxCurrentCount ) | |
{ | |
/* The logging produced below may be helpful | |
while tuning +TCP: see how many buffers are in use. */ | |
uxLastMinQueueSpace = uxCurrentCount; | |
FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); | |
} | |
} | |
#endif /* ipconfigCHECK_IP_QUEUE_SPACE */ | |
if( ( xEMACpsif.isr_events & EMAC_IF_ALL_EVENT ) == 0 ) | |
{ | |
/* No events to process now, wait for the next. */ | |
ulTaskNotifyTake( pdFALSE, ulMaxBlockTime ); | |
} | |
if( ( xEMACpsif.isr_events & EMAC_IF_RX_EVENT ) != 0 ) | |
{ | |
xEMACpsif.isr_events &= ~EMAC_IF_RX_EVENT; | |
xResult = emacps_check_rx( &xEMACpsif ); | |
} | |
if( ( xEMACpsif.isr_events & EMAC_IF_TX_EVENT ) != 0 ) | |
{ | |
xEMACpsif.isr_events &= ~EMAC_IF_TX_EVENT; | |
emacps_check_tx( &xEMACpsif ); | |
} | |
if( ( xEMACpsif.isr_events & EMAC_IF_ERR_EVENT ) != 0 ) | |
{ | |
xEMACpsif.isr_events &= ~EMAC_IF_ERR_EVENT; | |
emacps_check_errors( &xEMACpsif ); | |
} | |
if( xResult > 0 ) | |
{ | |
/* A packet was received. No need to check for the PHY status now, | |
but set a timer to check it later on. */ | |
vTaskSetTimeOutState( &xPhyTime ); | |
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); | |
xResult = 0; | |
} | |
else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) | |
{ | |
xStatus = ulReadMDIO( PHY_REG_01_BMSR ); | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) ) | |
{ | |
ulPHYLinkStatus = xStatus; | |
FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) ); | |
} | |
vTaskSetTimeOutState( &xPhyTime ); | |
if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) | |
{ | |
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); | |
} | |
else | |
{ | |
xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); | |
} | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ |