/* $Id: xemac_intr_fifo_example.c,v 1.1 2003/09/26 22:04:51 moleres Exp $ */ /****************************************************************************** * * **************************** * ** Copyright Xilinx, Inc. ** * ** All rights reserved. ** * **************************** * * FILENAME: * * xemac_intr_fifo_example.c * * DESCRIPTION: * * Contains an example of how to use the XEmac driver directly. The example here * shows using the driver/device in interrupt mode with direct FIFO I/O. This * mode is typically not used when the device is configured with DMA. * * NOTES: * * The following symbol controls some of the code for this example. * * USE_BSP_VXWORKS Define when a VxWorks BSP sets up interrupt handling. It * assumes the BSP defines intConnect and intEnable functions. * USE_XINTC Define when the Xilinx interrupt controller (XIntc) is to be * used directly in this example. This assumes a BSP does * not handle the XIntc initialization or use. * USE_XINTC_VXWORKS Define when the XIntc is used directly in this example, and * VxWorks on the PowerPC is available for interrupt handling. * ******************************************************************************/ /***************************** Include Files *********************************/ #include "xemac.h" #include "xparameters.h" #include "xstatus.h" #define PPC405 #define USE_XINTC #ifdef MICROBLAZE #include "mb_interface.h" #endif #ifdef PPC405 /* No OS */ #include "xexception_l.h" #endif #ifdef USE_BSP_VXWORKS #include "config.h" #include "intLib.h" #endif #ifdef USE_XINTC_VXWORKS #include "vxWorks.h" #include "excLib.h" #endif #ifdef USE_XINTC #include "xintc.h" #endif /************************** Constant Definitions *****************************/ #define XEM_MAX_FRAME_SIZE_IN_WORDS ((XEM_MAX_FRAME_SIZE / sizeof(Xuint32)) + 1) #define DEFAULT_OPTIONS (XEM_UNICAST_OPTION | XEM_INSERT_PAD_OPTION | \ XEM_INSERT_FCS_OPTION | XEM_INSERT_ADDR_OPTION | \ XEM_OVWRT_ADDR_OPTION) /**************************** Type Definitions *******************************/ /***************** Macros (Inline Functions) Definitions *********************/ /************************** Function Prototypes ******************************/ static XStatus LoopbackFrame(XEmac *InstancePtr, Xuint32 PayloadSize); static XStatus SendFrame(XEmac *InstancePtr, Xuint32 PayloadSize, Xuint8 *DestAddress); static void FifoRecvHandler(void *CallBackRef); static void FifoSendHandler(void *CallBackRef); static void ErrorHandler(void *CallBackRef, XStatus Code); static XStatus SetupInterruptSystem(XEmac *InstancePtr); /************************** Variable Definitions *****************************/ /* * Set up valid local MAC address. The loopback test will use the * LocalAddress both as source and destination. */ static Xuint8 LocalAddress[XEM_MAC_ADDR_SIZE] = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; /* * Allocate an instance of the XEmac driver */ static XEmac Emac; /* * These buffers need to be 32-bit aligned */ static Xuint32 TxFrame[XEM_MAX_FRAME_SIZE_IN_WORDS]; static Xuint32 RecvBuffer[XEM_MAX_FRAME_SIZE_IN_WORDS]; /* * Shared variables used to test the callbacks with the send/receive */ static int SentPayloadSize; /* Outstanding frame length */ static XStatus LoopbackError; /* Asynchronous error occurred */ volatile static Xboolean RecvDone; /* Received a frame */ volatile static Xboolean SendDone; /* Sent a frame */ int main() { (void)XEmacIntrFifoExample(); return 0; } /****************************************************************************** * * FUNCTION: * * XEmacIntrFifoExample * * DESCRIPTION: * * The main entry point for showing the XEmac driver in interrupt mode with * FIFOs. The example configures the device for internal loopback mode, then * sends an Ethernet frame and receives the same Ethernet frame. * * ARGUMENTS: * * None. * * RETURN VALUE: * * 0 if successful, otherwise the number of errors that occurred. * * NOTES: * * None. * ******************************************************************************/ int XEmacIntrFifoExample() { XEmac *InstancePtr = &Emac; Xuint16 DeviceId = XPAR_OPB_ETHERNET_ONE_DEVICE_ID; /* from xparameters.h */ XStatus Result; Xuint32 Options; /* device config options */ int ErrorCnt = 0; XEmac_Config *ConfigPtr; /* * We change the configuration of the device to indicate no DMA just in * case it was built with DMA. In order for this example to work, you * either need to do this or, better yet, build the hardware without DMA. */ ConfigPtr = XEmac_LookupConfig(DeviceId); ConfigPtr->IpIfDmaConfig = XEM_CFG_NO_DMA; /* * Initialize the XEmac component. The device ID chosen should be * configured without DMA (see the xemac_g.c file) */ Result = XEmac_Initialize(InstancePtr, DeviceId); if (Result != XST_SUCCESS) { return ++ErrorCnt; } /* * We can check to be sure the device is confgured without DMA before * going on with the example. */ if (XEmac_mIsDma(InstancePtr)) { /* OOPS! it has DMA */ return ++ErrorCnt; } /* * Run self-test on the device, which verifies basic sanity of the * device and the driver. */ Result = XEmac_SelfTest(InstancePtr); if (Result != XST_SUCCESS) { return ++ErrorCnt; } /* * First configure the device into loopback mode. We also configure it * for unicast and broadcast addressing. Also, set the MAC address. */ Options = (DEFAULT_OPTIONS | XEM_BROADCAST_OPTION | XEM_LOOPBACK_OPTION); Result = XEmac_SetOptions(InstancePtr, Options); if (Result != XST_SUCCESS) { return ++ErrorCnt; } Result = XEmac_SetMacAddress(InstancePtr, LocalAddress); if (Result != XST_SUCCESS) { return ++ErrorCnt; } /* * Set the FIFO callbacks and error handler. These callbacks are invoked * by the driver during interrupt processing. */ XEmac_SetFifoSendHandler(InstancePtr, InstancePtr, FifoSendHandler); XEmac_SetFifoRecvHandler(InstancePtr, InstancePtr, FifoRecvHandler); XEmac_SetErrorHandler(InstancePtr, InstancePtr, ErrorHandler); /* * Connect to the interrupt controller and enable interrupts */ Result = SetupInterruptSystem(InstancePtr); if (Result != XST_SUCCESS) { return ++ErrorCnt; } /* * Start the device, which enables the transmitter and receiver */ Result = XEmac_Start(InstancePtr); if (Result != XST_SUCCESS) { return ++ErrorCnt; } /* * We clear the driver statistics so we can look at them later */ XEmac_ClearStats(InstancePtr); /* * Loopback a 200-byte frame (214 with the Ethernet header). */ Result = LoopbackFrame(InstancePtr, 200); if (Result != XST_SUCCESS) { return ++ErrorCnt; } return ErrorCnt; } /****************************************************************************** * * FUNCTION: * * LoopbackFrame * * DESCRIPTION: * * Loopback a frame of given size. This function assumes interrupt mode and * loopback mode and sends the frame. The FifoRecvHandler is expected to handle * the received frame. * * ARGUMENTS: * * PayloadSize is the size of the frame to create. The size only reflects the * payload size, it does not include the Ethernet header size (14 bytes) nor * the Ethernet CRC size (4 bytes). * * RETURN VALUE: * * XST_SUCCESS if successful, a driver-specific return code if the send * or recieve fails. * * NOTES: * * None. * ******************************************************************************/ static XStatus LoopbackFrame(XEmac *InstancePtr, Xuint32 PayloadSize) { XStatus Result; SendDone = XFALSE; RecvDone = XFALSE; LoopbackError = XST_SUCCESS; SentPayloadSize = PayloadSize; Result = SendFrame(InstancePtr, PayloadSize, LocalAddress); if (Result == XST_SUCCESS) { /* Wait here until both send and receive have been completed */ while (!SendDone || !RecvDone); /* * Check for errors found in the callbacks */ if (LoopbackError != XST_SUCCESS) { return LoopbackError; } } return Result; } /****************************************************************************** * * FUNCTION: * * SendFrame * * DESCRIPTION: * * Send an Ethernet frame of given size * * ARGUMENTS: * * PayloadSize is the size of the frame to create. The size only reflects the * payload size, it does not include the Ethernet header size (14 bytes) nor * the Ethernet CRC size (4 bytes). * * RETURN VALUE: * * XST_SUCCESS if successful, a driver-specific return code if not * * NOTES: * * None. * ******************************************************************************/ static XStatus SendFrame(XEmac *InstancePtr, Xuint32 PayloadSize, Xuint8 *DestAddress) { Xuint8 *FramePtr; Xuint8 *AddrPtr = DestAddress; int Index; /* * Assemble the frame with a destination address and a bogus source address * (the MAC overwrites it by default) */ FramePtr = (Xuint8 *)TxFrame; *FramePtr++ = *AddrPtr++; *FramePtr++ = *AddrPtr++; *FramePtr++ = *AddrPtr++; *FramePtr++ = *AddrPtr++; *FramePtr++ = *AddrPtr++; *FramePtr++ = *AddrPtr++; FramePtr += XEM_MAC_ADDR_SIZE; /* get past source address (bogus) */ /* Set up the type/length field - be sure its in network order */ *((Xuint16 *)FramePtr) = PayloadSize; FramePtr++; FramePtr++; /* * Now fill in the data field with known values so we can verify them * on receive. */ for (Index = 0; Index < PayloadSize; Index++) { *FramePtr++ = (Xuint8)Index; } /* * Now send the frame (payload and header) */ return XEmac_FifoSend(InstancePtr, (Xuint8 *)TxFrame, PayloadSize + XEM_HDR_SIZE); } /****************************************************************************** * * FUNCTION: * * FifoRecvHandler * * DESCRIPTION: * * Callback function (called from driver) to handle frames received in direct * memory-mapped I/O mode. This function is called once per frame received. * It notifies me that there is data to be retrieved from the driver. The * driver's receive function is called to get the data. * * ARGUMENTS: * * CallBackRef is the callback reference passed from the driver, which in our * case is a pointer to a DriverInfo structure. * * RETURN VALUE: * * None. * * NOTES: * * This function is called by the driver within interrupt context. * ******************************************************************************/ static void FifoRecvHandler(void *CallBackRef) { XEmac *EmacPtr = (XEmac *)CallBackRef; Xuint32 FrameLen; XStatus Result; int Index; Xuint8 *FramePtr; /* * Retrieve the frame from the EMAC */ FrameLen = XEM_MAX_FRAME_SIZE; Result = XEmac_FifoRecv(EmacPtr, (Xuint8 *)RecvBuffer, &FrameLen); if (Result != XST_SUCCESS) { LoopbackError = Result; RecvDone = XTRUE; return; } /* * Verify the frame is the correct length. SentPayloadSize is a shared * variable. */ if (FrameLen != (SentPayloadSize + XEM_HDR_SIZE + XEM_TRL_SIZE)) { LoopbackError = XST_LOOPBACK_ERROR; RecvDone = XTRUE; return; } /* * Verify the frame contents */ FramePtr = (Xuint8 *)RecvBuffer; FramePtr += XEM_HDR_SIZE; /* get past the header */ for (Index = 0; Index < SentPayloadSize; Index++) { if (*FramePtr++ != (Xuint8)Index) { LoopbackError = XST_LOOPBACK_ERROR; break; } } RecvDone = XTRUE; } /****************************************************************************** * * FUNCTION: * * FifoSendHandler * * DESCRIPTION: * * Callback function (called from driver) to handle confirmation of transmit * events when in direct memory-mapped I/O mode. * * ARGUMENTS: * * CallBackRef is the callback reference passed from the driver, which in our * case is a pointer the driver instance. * * RETURN VALUE: * * None. * * NOTES: * * None. * ******************************************************************************/ static void FifoSendHandler(void *CallBackRef) { XEmac *EmacPtr = (XEmac *)CallBackRef; XEmac_Stats Stats; /* * Check stats for transmission errors (overrun or underrun errors are * caught by the asynchronous error handler). */ XEmac_GetStats(EmacPtr, &Stats); if (Stats.XmitLateCollisionErrors || Stats.XmitExcessDeferral) { LoopbackError = XST_LOOPBACK_ERROR; } SendDone = XTRUE; } /****************************************************************************** * * FUNCTION: * * ErrorHandler * * DESCRIPTION: * * Callback function (called from driver) to handle asynchronous errors. These * errors are usually bad and require a reset of the device. Here is an * example of how to recover (albeit data will be lost during the reset) * * ARGUMENTS: * * CallBackRef is the callback reference passed from the driver, which in our * case is a pointer the driver instance. * * RETURN VALUE: * * None. * * NOTES: * * None. * ******************************************************************************/ static void ErrorHandler(void *CallBackRef, XStatus Code) { XEmac *EmacPtr = (XEmac *)CallBackRef; LoopbackError = Code; /* Set the shared variable */ /* * Most if not all asynchronous errors returned by the XEmac driver are * serious. The usual remedy is to reset the device. The user will need * to re-configure the driver/device after the reset, so if you don't know * how it is configured, be sure to retrieve its options and configuration * (e.g., using the XEmac_Get... functions) before the reset. */ if (Code == XST_RESET_ERROR) { /* May want to set a breakpoint here during debugging */ XEmac_Reset(EmacPtr); (void)XEmac_SetMacAddress(EmacPtr, LocalAddress); (void)XEmac_SetOptions(EmacPtr, DEFAULT_OPTIONS); (void)XEmac_Start(EmacPtr); } } /**************************************************************************** * * FUNCTION: * * SetupInterruptSystem * * DESCRIPTION: * * This function setups the interrupt system so interrupts can occur for the * EMAC. This function is application-specific since the actual system may or * may not have an interrupt controller. The EMAC could be directly connected * to a processor without an interrupt controller. The user should modify this * function to fit the application. * * ARGUMENTS: * * InstancePtr contains a pointer to the instance of the EMAC component which * is going to be connected to the interrupt controller. * * RETURN VALUE: * * XST_SUCCESS if successful, otherwise XST_FAILURE. * * NOTES: * * ****************************************************************************/ static XStatus SetupInterruptSystem(XEmac *InstancePtr) { #ifdef USE_BSP_VXWORKS intConnect(INUM_TO_IVEC(XPAR_OPB_INTC_ONE_OPB_ETHERNET_ONE_IP2INTC_IRPT_INTR), (VOIDFUNCPTR)XEmac_IntrHandlerFifo, (int)InstancePtr); intEnable(XPAR_OPB_INTC_ONE_OPB_ETHERNET_ONE_IP2INTC_IRPT_INTR); #else #ifdef USE_XINTC /* use the Xilinx interrupt controller */ static XIntc InterruptController; XStatus Result; #ifdef MICROBLAZE microblaze_enable_interrupts(); #endif #ifdef PPC405 /* * Initialize the PPC405 exception table */ XExc_Init(); /* * Register the interrupt controller handler with the exception table */ XExc_RegisterHandler(XEXC_ID_NON_CRITICAL_INT, (XExceptionHandler)XIntc_InterruptHandler, &InterruptController); /* * Enable non-critical exceptions */ XExc_mEnableExceptions(XEXC_NON_CRITICAL); #endif /* * Initialize the interrupt controller driver so that it's ready to use. * Xpecify the device ID that is generated in xparameters.h */ Result = XIntc_Initialize(&InterruptController, XPAR_OPB_INTC_ONE_DEVICE_ID); if (Result != XST_SUCCESS) { return Result; } #ifdef USE_XINTC_VXWORKS /* VxWorks on the PowerPC platform only */ /* connect the handler to the VxWorks non-critical external interrupt */ excIntConnect((VOIDFUNCPTR *)_EXC_OFF_INTR, (VOIDFUNCPTR)XIntc_VoidInterruptHandler); #endif /* * Connect the device driver handler that will be called when an interrupt * for the device occurs, the device driver handler performs the specific * interrupt processing for the device */ Result = XIntc_Connect(&InterruptController, XPAR_OPB_INTC_ONE_OPB_ETHERNET_ONE_IP2INTC_IRPT_INTR, (XInterruptHandler)XEmac_IntrHandlerFifo, InstancePtr); if (Result != XST_SUCCESS) { return Result; } /* * Start the interrupt controller so interrupts are enabled for all * devices that cause interrupts. Specify real mode so that the EMAC * can cause interrupts through the interrupt controller. */ Result = XIntc_Start(&InterruptController, XIN_REAL_MODE); if (Result != XST_SUCCESS) { return Result; } /* Enable the interrupt for the EMAC */ XIntc_Enable(&InterruptController, XPAR_OPB_INTC_ONE_OPB_ETHERNET_ONE_IP2INTC_IRPT_INTR); #endif /* USE_XINTC */ #endif /* USE_BSP_VXWORKS */ return XST_SUCCESS; }