#define TESTAPP_GEN /****************************************************************************** * * Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Use of the Software is limited solely to applications: * (a) running on a Xilinx device, or * (b) that interact with a Xilinx device through a bus or interconnect. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of the Xilinx shall not be used * in advertising or otherwise to promote the sale, use or other dealings in * this Software without prior written authorization from Xilinx. * ******************************************************************************/ /** * * @file xaxiethernet_example_util.c * * This file implements the utility functions for the Axi Ethernet example code. * *
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -------------------------------------------------------
* 1.00a asa  6/30/10 First release based on the ll temac driver
* 3.01a srt  02/03/13 Added support for SGMII mode (CR 676793).
*	     02/14/13 Added support for Zynq (CR 681136).
* 3.02a srt  04/24/13 Modified parameter *_SGMII_PHYADDR to *_PHYADDR, the
*                     config parameter C_PHYADDR applies to SGMII/1000BaseX
*	              modes of operation and added support for 1000BaseX mode
*		      (CR 704195). Added function *_ConfigureInternalPhy()
*		      for this purpose.
*	     04/24/13 Added support for RGMII mode.
* 3.02a srt  08/06/13 Fixed CR 717949:
*			Configures external Marvel 88E1111 PHY based on the
*			axi ethernet physical interface type and allows to
*			operate in specific interface mode without changing
*			jumpers on the Microblaze board.
*
* 
* ******************************************************************************/ /***************************** Include Files *********************************/ #include "xaxiethernet_example.h" #if !defined (__MICROBLAZE__) && !defined(__PPC__) #include "sleep.h" #endif /************************** Variable Definitions ****************************/ /* * Local MAC address */ char AxiEthernetMAC[6] = { 0x00, 0x0A, 0x35, 0x01, 0x02, 0x03 }; /******************************************************************************/ /** * * Set the MAC addresses in the frame. * * @param FramePtr is the pointer to the frame. * @param DestAddr is the Destination MAC address. * * @return None. * * @note None. * ******************************************************************************/ void AxiEthernetUtilFrameHdrFormatMAC(EthernetFrame *FramePtr, char *DestAddr) { char *Frame = (char *) FramePtr; char *SourceAddress = AxiEthernetMAC; int Index; /* * Destination address */ for (Index = 0; Index < XAE_MAC_ADDR_SIZE; Index++) { *Frame++ = *DestAddr++; } /* * Source address */ for (Index = 0; Index < XAE_MAC_ADDR_SIZE; Index++) { *Frame++ = *SourceAddress++; } } /******************************************************************************/ /** * * Set the frame type for the specified frame. * * @param FramePtr is the pointer to the frame. * @param FrameType is the Type to set in frame. * * @return None. * * @note None. * ******************************************************************************/ void AxiEthernetUtilFrameHdrFormatType(EthernetFrame *FramePtr, u16 FrameType) { char *Frame = (char *) FramePtr; /* * Increment to type field */ Frame = Frame + 12; FrameType = Xil_Htons(FrameType); /* * Set the type */ *(u16 *) Frame = FrameType; } /******************************************************************************/ /** * This function places a pattern in the payload section of a frame. The pattern * is a 8 bit incrementing series of numbers starting with 0. * Once the pattern reaches 256, then the pattern changes to a 16 bit * incrementing pattern: *
*   0, 1, 2, ... 254, 255, 00, 00, 00, 01, 00, 02, ...
* 
* * @param FramePtr is a pointer to the frame to change. * @param PayloadSize is the number of bytes in the payload that will be * set. * * @return None. * * @note None. * ******************************************************************************/ void AxiEthernetUtilFrameSetPayloadData(EthernetFrame *FramePtr, int PayloadSize) { unsigned BytesLeft = PayloadSize; u8 *Frame; u16 Counter = 0; /* * Set the frame pointer to the start of the payload area */ Frame = (u8 *) FramePtr + XAE_HDR_SIZE; /* * Insert 8 bit incrementing pattern */ while (BytesLeft && (Counter < 256)) { *Frame++ = (u8) Counter++; BytesLeft--; } /* * Switch to 16 bit incrementing pattern */ while (BytesLeft) { *Frame++ = (u8) (Counter >> 8); /* high */ BytesLeft--; if (!BytesLeft) break; *Frame++ = (u8) Counter++; /* low */ BytesLeft--; } } /******************************************************************************/ /** * * Set the frame VLAN info for the specified frame. * * @param FramePtr is the pointer to the frame. * @param VlanNumber is the VlanValue insertion position to set in frame. * @param Vid is the 4 bytes Vlan value (TPID, Priority, CFI, VID) * to be set in frame. * * @return None. * * @note None. * ******************************************************************************/ void AxiEthernetUtilFrameHdrVlanFormatVid(EthernetFrame *FramePtr, u32 VlanNumber, u32 Vid) { char *Frame = (char *) FramePtr; /* * Increment to type field */ Frame = Frame + 12 + (VlanNumber * 4); Vid = Xil_Htonl(Vid); /* * Set the type */ *(u32 *) Frame = Vid; } /******************************************************************************/ /** * * Set the frame type for the specified frame. * * @param FramePtr is the pointer to the frame. * @param FrameType is the Type to set in frame. * @param VlanNumber is the VLAN friendly adjusted insertion position to * set in frame. * * @return None. * * @note None. * ******************************************************************************/ void AxiEthernetUtilFrameHdrVlanFormatType(EthernetFrame *FramePtr, u16 FrameType, u32 VlanNumber) { char *Frame = (char *) FramePtr; /* * Increment to type field */ Frame = Frame + 12 + (VlanNumber * 4); FrameType = Xil_Htons(FrameType); /* * Set the type */ *(u16 *) Frame = FrameType; } /******************************************************************************/ /** * This function places a pattern in the payload section of a frame. The pattern * is a 8 bit incrementing series of numbers starting with 0. * Once the pattern reaches 256, then the pattern changes to a 16 bit * incrementing pattern: *
*   0, 1, 2, ... 254, 255, 00, 00, 00, 01, 00, 02, ...
* 
* * @param FramePtr is a pointer to the frame to change. * @param PayloadSize is the number of bytes in the payload that will be set. * @param VlanNumber is the VLAN friendly adjusted insertion position to * set in frame. * * @return None. * * @note None. * ******************************************************************************/ void AxiEthernetUtilFrameSetVlanPayloadData(EthernetFrame *FramePtr, int PayloadSize, u32 VlanNumber) { unsigned BytesLeft = PayloadSize; u8 *Frame; u16 Counter = 0; /* * Set the frame pointer to the start of the payload area */ Frame = (u8 *) FramePtr + XAE_HDR_SIZE + (VlanNumber * 4); /* * Insert 8 bit incrementing pattern */ while (BytesLeft && (Counter < 256)) { *Frame++ = (u8) Counter++; BytesLeft--; } /* * Switch to 16 bit incrementing pattern */ while (BytesLeft) { *Frame++ = (u8) (Counter >> 8); /* high */ BytesLeft--; if (!BytesLeft) break; *Frame++ = (u8) Counter++; /* low */ BytesLeft--; } } /******************************************************************************/ /** * This function verifies the frame data against a CheckFrame. * * Validation occurs by comparing the ActualFrame to the header of the * CheckFrame. If the headers match, then the payload of ActualFrame is * verified for the same pattern Util_FrameSetPayloadData() generates. * * @param CheckFrame is a pointer to a frame containing the 14 byte header * that should be present in the ActualFrame parameter. * @param ActualFrame is a pointer to a frame to validate. * * @return - XST_SUCCESS if successful. * - XST_FAILURE in case of failure. * * @note None. * ******************************************************************************/ int AxiEthernetUtilFrameVerify(EthernetFrame * CheckFrame, EthernetFrame * ActualFrame) { unsigned char *CheckPtr = (unsigned char *) CheckFrame; unsigned char *ActualPtr = (unsigned char *) ActualFrame; u16 BytesLeft; u16 Counter; int Index; /* * Compare the headers */ for (Index = 0; Index < XAE_HDR_SIZE; Index++) { if (CheckPtr[Index] != ActualPtr[Index]) { return XST_FAILURE; } } Index = 0; BytesLeft = *(u16 *) &ActualPtr[12]; BytesLeft = Xil_Ntohs(BytesLeft); /* * Get the length of the payload, do not use VLAN TPID here. * TPID needs to be verified. */ while ((0x8100 == BytesLeft) || (0x88A8 == BytesLeft) || (0x9100 == BytesLeft) || (0x9200 == BytesLeft)) { Index++; BytesLeft = *(u16 *) &ActualPtr[12+(4*Index)]; BytesLeft = Xil_Ntohs(BytesLeft); } /* * Validate the payload */ Counter = 0; ActualPtr = &ActualPtr[14+(4*Index)]; /* * Check 8 bit incrementing pattern */ while (BytesLeft && (Counter < 256)) { if (*ActualPtr++ != (u8) Counter++) { return XST_FAILURE; } BytesLeft--; } /* * Check 16 bit incrementing pattern */ while (BytesLeft) { if (*ActualPtr++ != (u8) (Counter >> 8)) { /* high */ return XST_FAILURE; } BytesLeft--; if (!BytesLeft) break; if (*ActualPtr++ != (u8) Counter++) { /* low */ return XST_FAILURE; } BytesLeft--; } return XST_SUCCESS; } /******************************************************************************/ /** * This function sets all bytes of a frame to 0. * * @param FramePtr is a pointer to the frame itself. * * @return None. * * @note None. * ******************************************************************************/ void AxiEthernetUtilFrameMemClear(EthernetFrame * FramePtr) { u32 *Data32Ptr = (u32 *) FramePtr; u32 WordsLeft = sizeof(EthernetFrame) / sizeof(u32); /* * Frame should be an integral number of words */ while (WordsLeft--) { *Data32Ptr++ = 0; } } /******************************************************************************/ /** * * This function detects the PHY address by looking for successful MII status * register contents (PHY register 1). It looks for a PHY that supports * auto-negotiation and 10Mbps full-duplex and half-duplex. So, this code * won't work for PHYs that don't support those features, but it's a bit more * general purpose than matching a specific PHY manufacturer ID. * * Note also that on some (older) Xilinx ML4xx boards, PHY address 0 does not * properly respond to this query. But, since the default is 0 and asssuming * no other address responds, then it seems to work OK. * * @param The Axi Ethernet driver instance * * @return The address of the PHY (defaults to 0 if none detected) * * @note None. * ******************************************************************************/ /* Use MII register 1 (MII status register) to detect PHY */ #define PHY_DETECT_REG 1 /* Mask used to verify certain PHY features (or register contents) * in the register above: * 0x1000: 10Mbps full duplex support * 0x0800: 10Mbps half duplex support * 0x0008: Auto-negotiation support */ #define PHY_DETECT_MASK 0x1808 u32 AxiEthernetDetectPHY(XAxiEthernet * AxiEthernetInstancePtr) { u16 PhyReg; int PhyAddr; for (PhyAddr = 31; PhyAddr >= 0; PhyAddr--) { XAxiEthernet_PhyRead(AxiEthernetInstancePtr, PhyAddr, PHY_DETECT_REG, &PhyReg); if ((PhyReg != 0xFFFF) && ((PhyReg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { /* Found a valid PHY address */ return PhyAddr; } } return 0; /* Default to zero */ } /******************************************************************************/ /** * Set PHY to loopback mode. This works with the marvell PHY common on ML40x * evaluation boards * * @param Speed is the loopback speed 10, 100, or 1000 Mbit * ******************************************************************************/ /* IEEE PHY Specific definitions */ #define PHY_R0_CTRL_REG 0 #define PHY_R3_PHY_IDENT_REG 3 #define PHY_R0_RESET 0x8000 #define PHY_R0_LOOPBACK 0x4000 #define PHY_R0_ANEG_ENABLE 0x1000 #define PHY_R0_DFT_SPD_MASK 0x2040 #define PHY_R0_DFT_SPD_10 0x0000 #define PHY_R0_DFT_SPD_100 0x2000 #define PHY_R0_DFT_SPD_1000 0x0040 #define PHY_R0_ISOLATE 0x0400 /* Marvel PHY 88E1111 Specific definitions */ #define PHY_R20_EXTND_CTRL_REG 20 #define PHY_R27_EXTND_STS_REG 27 #define PHY_R20_DFT_SPD_10 0x20 #define PHY_R20_DFT_SPD_100 0x50 #define PHY_R20_DFT_SPD_1000 0x60 #define PHY_R20_RX_DLY 0x80 #define PHY_R27_MAC_CONFIG_GMII 0x000F #define PHY_R27_MAC_CONFIG_MII 0x000F #define PHY_R27_MAC_CONFIG_RGMII 0x000B #define PHY_R27_MAC_CONFIG_SGMII 0x0004 /* Marvel PHY 88E1116R Specific definitions */ #define PHY_R22_PAGE_ADDR_REG 22 #define PHY_PG2_R21_CTRL_REG 21 #define PHY_REG21_10 0x0030 #define PHY_REG21_100 0x2030 #define PHY_REG21_1000 0x0070 /* Marvel PHY flags */ #define MARVEL_PHY_88E1111_MODEL 0xC0 #define MARVEL_PHY_88E1116R_MODEL 0x240 #define PHY_MODEL_NUM_MASK 0x3F0 /******************************************************************************/ /** * * This function sets the PHY to loopback mode. This works with the marvell PHY * common on ML40x evaluation boards. * * @param AxiEthernetInstancePtr is a pointer to the instance of the * AxiEthernet component. * @param Speed is the loopback speed 10, 100, or 1000 Mbit. * * @return - XST_SUCCESS if successful. * - XST_FAILURE, in case of failure.. * * @note None. * ******************************************************************************/ int AxiEthernetUtilEnterLoopback(XAxiEthernet *AxiEthernetInstancePtr, int Speed) { u16 PhyReg0; signed int PhyAddr; u8 PhyType; u16 PhyModel; u16 PhyReg20; /* Extended PHY specific Register (Reg 20) of Marvell 88E1111 PHY */ u16 PhyReg21; /* Control Register MAC (Reg 21) of Marvell 88E1116R PHY */ /* Get the Phy Interface */ PhyType = XAxiEthernet_GetPhysicalInterface(AxiEthernetInstancePtr); /* Detect the PHY address */ if (PhyType != XAE_PHY_TYPE_1000BASE_X) { PhyAddr = AxiEthernetDetectPHY(AxiEthernetInstancePtr); } else { PhyAddr = XPAR_AXIETHERNET_0_PHYADDR; } XAxiEthernet_PhyRead(AxiEthernetInstancePtr, PhyAddr, PHY_R3_PHY_IDENT_REG, &PhyModel); PhyModel = PhyModel & PHY_MODEL_NUM_MASK; /* Clear the PHY of any existing bits by zeroing this out */ PhyReg0 = PhyReg20 = PhyReg21 = 0; switch (Speed) { case XAE_SPEED_10_MBPS: PhyReg0 |= PHY_R0_DFT_SPD_10; PhyReg20 |= PHY_R20_DFT_SPD_10; PhyReg21 |= PHY_REG21_10; break; case XAE_SPEED_100_MBPS: PhyReg0 |= PHY_R0_DFT_SPD_100; PhyReg20 |= PHY_R20_DFT_SPD_100; PhyReg21 |= PHY_REG21_100; break; case XAE_SPEED_1000_MBPS: PhyReg0 |= PHY_R0_DFT_SPD_1000; PhyReg20 |= PHY_R20_DFT_SPD_1000; PhyReg21 |= PHY_REG21_1000; break; default: AxiEthernetUtilErrorTrap("Intg_LinkSpeed not 10, 100, or 1000 mbps"); return XST_FAILURE; } /* RGMII mode Phy specific registers initialization */ if ((PhyType == XAE_PHY_TYPE_RGMII_2_0) || (PhyType == XAE_PHY_TYPE_RGMII_1_3)) { if (PhyModel == MARVEL_PHY_88E1111_MODEL) { PhyReg20 |= PHY_R20_RX_DLY; /* * Adding Rx delay. Configuring loopback speed. */ XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R20_EXTND_CTRL_REG, PhyReg20); } else if (PhyModel == MARVEL_PHY_88E1116R_MODEL) { /* * Switching to PAGE2 */ XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R22_PAGE_ADDR_REG, 2); /* * Adding Tx and Rx delay. Configuring loopback speed. */ XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_PG2_R21_CTRL_REG, PhyReg21); /* * Switching to PAGE0 */ XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R22_PAGE_ADDR_REG, 0); } PhyReg0 &= (~PHY_R0_ANEG_ENABLE); } /* Configure interface modes */ if (PhyModel == MARVEL_PHY_88E1111_MODEL) { if ((PhyType == XAE_PHY_TYPE_RGMII_2_0) || (PhyType == XAE_PHY_TYPE_RGMII_1_3)) { XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R27_EXTND_STS_REG, PHY_R27_MAC_CONFIG_RGMII); } else if (PhyType == XAE_PHY_TYPE_SGMII) { XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R27_EXTND_STS_REG, PHY_R27_MAC_CONFIG_SGMII); } else if ((PhyType == XAE_PHY_TYPE_GMII) || (PhyType == XAE_PHY_TYPE_MII)) { XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R27_EXTND_STS_REG, PHY_R27_MAC_CONFIG_GMII ); } } /* Set the speed and put the PHY in reset, then put the PHY in loopback */ XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R0_CTRL_REG, PhyReg0 | PHY_R0_RESET); AxiEthernetUtilPhyDelay(AXIETHERNET_PHY_DELAY_SEC); XAxiEthernet_PhyRead(AxiEthernetInstancePtr,PhyAddr, PHY_R0_CTRL_REG, &PhyReg0); XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R0_CTRL_REG, PhyReg0 | PHY_R0_LOOPBACK); if ((PhyType == XAE_PHY_TYPE_SGMII) || (PhyType == XAE_PHY_TYPE_1000BASE_X)) { AxiEthernetUtilConfigureInternalPhy(AxiEthernetInstancePtr, Speed); } AxiEthernetUtilPhyDelay(1); return XST_SUCCESS; } /******************************************************************************/ /** * * This function is called by example code when an error is detected. It * can be set as a breakpoint with a debugger or it can be used to print out the * given message if there is a UART or STDIO device. * * @param Message is the text explaining the error * * @return None * * @note None * ******************************************************************************/ void AxiEthernetUtilErrorTrap(char *Message) { static int Count = 0; Count++; #ifdef STDOUT_BASEADDRESS xil_printf("%s\r\n", Message); #endif } /******************************************************************************/ /** * * For Microblaze we use an assembly loop that is roughly the same regardless of * optimization level, although caches and memory access time can make the delay * vary. Just keep in mind that after resetting or updating the PHY modes, * the PHY typically needs time to recover. * * @return None * * @note None * ******************************************************************************/ void AxiEthernetUtilPhyDelay(unsigned int Seconds) { #if defined (__MICROBLAZE__) || defined(__PPC__) static int WarningFlag = 0; /* If MB caches are disabled or do not exist, this delay loop could * take minutes instead of seconds (e.g., 30x longer). Print a warning * message for the user (once). If only MB had a built-in timer! */ if (((mfmsr() & 0x20) == 0) && (!WarningFlag)) { WarningFlag = 1; } #define ITERS_PER_SEC (XPAR_CPU_CORE_CLOCK_FREQ_HZ / 6) asm volatile ("\n" "1: \n\t" "addik r7, r0, %0 \n\t" "2: \n\t" "addik r7, r7, -1 \n\t" "bneid r7, 2b \n\t" "or r0, r0, r0 \n\t" "bneid %1, 1b \n\t" "addik %1, %1, -1 \n\t" :: "i"(ITERS_PER_SEC), "d" (Seconds)); #else sleep(Seconds); #endif } /******************************************************************************/ /** * * This function configures the internal phy for SGMII and 1000baseX modes. * * * @param AxiEthernetInstancePtr is a pointer to the instance of the * AxiEthernet component. * @param Speed is the loopback speed 10, 100, or 1000 Mbit. * * @return - XST_SUCCESS if successful. * - XST_FAILURE, in case of failure.. * * @note None. * ******************************************************************************/ int AxiEthernetUtilConfigureInternalPhy(XAxiEthernet *AxiEthernetInstancePtr, int Speed) { u16 PhyReg0; signed int PhyAddr; PhyAddr = XPAR_AXIETHERNET_0_PHYADDR; /* Clear the PHY of any existing bits by zeroing this out */ PhyReg0 = 0; XAxiEthernet_PhyRead(AxiEthernetInstancePtr, PhyAddr, PHY_R0_CTRL_REG, &PhyReg0); PhyReg0 &= (~PHY_R0_ANEG_ENABLE); PhyReg0 &= (~PHY_R0_ISOLATE); switch (Speed) { case XAE_SPEED_10_MBPS: PhyReg0 |= PHY_R0_DFT_SPD_10; break; case XAE_SPEED_100_MBPS: PhyReg0 |= PHY_R0_DFT_SPD_100; break; case XAE_SPEED_1000_MBPS: PhyReg0 |= PHY_R0_DFT_SPD_1000; break; default: AxiEthernetUtilErrorTrap( "Intg_LinkSpeed not 10, 100, or 1000 mbps\n\r"); return XST_FAILURE; } AxiEthernetUtilPhyDelay(1); XAxiEthernet_PhyWrite(AxiEthernetInstancePtr, PhyAddr, PHY_R0_CTRL_REG, PhyReg0); return XST_SUCCESS; }