/****************************************************************************** * * Copyright (C) 2008 - 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 xavb_ptp_packets.c * * The XAvb driver. Functions in this file all contain functions which decode the * received Precise Timing Protocol (PTP) frames, or to format and transmit PTP * frames. * *
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a mbr  09/19/08 First release
* 1.01a mbr  06/24/09 PTP frame format updates for IEEE802.1 AS draft 5-0
* 2_02a mbr  09/16/09 Updates for programmable PTP timers
* 2_04a kag  07/23/10 PTP frame format updates for IEEE802.1 AS draft 6-7
* 3_01a kag  08/29/11 Added new APIs to update the RX Filter Control Reg.
*		      Fix for CR:572539. Updated bit map for Rx Filter
*		      control reg.
*
* 
* ******************************************************************************/ /***************************** Include Files *********************************/ #include "xil_types.h" #include "xavb_hw.h" #include "xavb.h" #include "stdlib.h" /************************** Constant Definitions *****************************/ /**************************** Type Definitions *******************************/ /***************** Macros (Inline Functions) Definitions *********************/ /************************** Variable Definitions *****************************/ /************************** Function Prototypes ******************************/ /*****************************************************************************/ /****************************************************************************/ /** * * A function to compare two ClockIdentity values. * * @param BaseAddress is the base address of the device * @param Identity1 is the first ClockIdentity to be compared * @param Identity2 is the second ClockIdentity to be compared * * @return 1 if the two values are equal, 0 if not equal * * @note * *****************************************************************************/ u32 XAvb_CompareClockIdentity(u32 BaseAddress, XAvb_ClockIdentity Identity1, XAvb_ClockIdentity Identity2) { if( Identity1.ClockIdentityUpper != Identity2.ClockIdentityUpper ) { /*xil_printf("ID1Upper (0x%08x) != ID2Upper (0x%08x)\r\n", Identity1.ClockIdentityUpper, Identity2.ClockIdentityUpper);*/ return 0; } if( Identity1.ClockIdentityLower != Identity2.ClockIdentityLower ) { /*xil_printf("ID1Lower (0x%08x) != ID2Lower (0x%08x)\r\n", Identity1.ClockIdentityLower, Identity2.ClockIdentityLower);*/ return 0; } /** values are equal */ return 1; } /****************************************************************************/ /** * * A function to compare two PortIdentity values. * * @param BaseAddress is the base address of the device * @param Identity1 is the first sourcePortIdentity to be compared * @param Identity2 is the second sourcePortIdentity to be compared * * @return 1 if the two values are equal, 0 if not equal * * @note None. * *****************************************************************************/ u32 XAvb_ComparePortIdentity(u32 BaseAddress, XAvb_PortIdentity Identity1, XAvb_PortIdentity Identity2) { if( Identity1.ClockIdentityUpper != Identity2.ClockIdentityUpper ) { /*xil_printf("ID1Upper (0x%08x) != ID2Upper (0x%08x)\r\n", Identity1.ClockIdentityUpper, Identity2.ClockIdentityUpper);*/ return 0; } if( Identity1.ClockIdentityLower != Identity2.ClockIdentityLower ) { /*xil_printf("ID1Lower (0x%08x) != ID2Lower (0x%08x)\r\n", Identity1.ClockIdentityLower, Identity2.ClockIdentityLower);*/ return 0; } if( Identity1.PortNumber != Identity2.PortNumber ) { /*xil_printf("ID1Port (0x%08x) != ID2Port (0x%08x)\r\n", Identity1.PortNumber, Identity2.PortNumber);*/ return 0; } /** values are equal */ return 1; } /****************************************************************************/ /** * * A function to extract portIdentity information from a received PTP frame. * This can be any portIdentity field (header portIdentity, requestingPortIdentity, * etc.) * * @param BaseAddress is the base address of the device * @param PtpFrameBaseAddr is the base address of the received Announce Packet * in the Rx PTP Packet Buffer * @param PortIdOffset is the packet offset of the first byte of the portIdentity * field to be parsed * @param portID is the XAvb_PortIdentity struct that the data will be written to. * * @return None, but portID will be updated with the portIdentity information * * @note None. * *****************************************************************************/ void XAvb_GetPortIdentity(u32 BaseAddress, u32 PtpFrameBaseAddr, u32 PortIdOffset, XAvb_PortIdentity *portID) { u32 ReadWord; ReadWord = XAvb_ReorderWord(XAvb_ReadPtpBuffer(BaseAddress, PtpFrameBaseAddr, PortIdOffset)); portID->ClockIdentityUpper = (ReadWord << 16); ReadWord = XAvb_ReorderWord(XAvb_ReadPtpBuffer(BaseAddress, PtpFrameBaseAddr, PortIdOffset + 4)); portID->ClockIdentityUpper |= (ReadWord >> 16); portID->ClockIdentityLower = (ReadWord << 16); ReadWord = XAvb_ReorderWord(XAvb_ReadPtpBuffer(BaseAddress, PtpFrameBaseAddr, PortIdOffset + 8)); portID->ClockIdentityLower |= (ReadWord >> 16); portID->PortNumber = ReadWord; } /****************************************************************************/ /** * * A function to write common data (eg the Source Address) to all PTP frames * stored in the Tx PTP Packet buffer * * @param BaseAddress is the base address of the device * @param PtpFieldAddress is the offset address of the relevant field in PTP * frames. * @param Data is the common data to be written to all Tx PTP frame templates * @param DataBitEnable allows only selected bits of the 32-bit Data word to * be modified. * @param BufferEnable allows the selected buffer to be seleced: there are 8 * PTP buffers - these are encoded as one-hot. For example, 0x3F will * write the selected data to the first 6 buffers only. * * @return None. But the Tx PTP Packet Buffer is written to as requested * * @note None. * *****************************************************************************/ void XAvb_WriteToMultipleTxPtpFrames(u32 BaseAddress, u32 PtpFieldAddress, u32 Data, u32 DataBitEnable, u8 BufferEnable) { u32 PtpBufferPointer = 0; u32 LocalData = 0; u32 LocalAddr = 0; /** Write to all 8 PTP frame templates */ for (PtpBufferPointer= 0; PtpBufferPointer < 8; PtpBufferPointer++) { /** Only write to selected buffers */ if ( ((BufferEnable >> PtpBufferPointer) & 0x1) == 0x1) { LocalAddr = (PtpBufferPointer<<8) + XAVB_PTP_TX_SYNC_OFFSET; /** Read the current value */ LocalData = XAvb_ReadPtpBuffer(BaseAddress, LocalAddr, PtpFieldAddress); /** Only change the selected data bits */ LocalData = LocalData | (Data & DataBitEnable); /** Write the updated value */ XAvb_WritePtpBuffer(BaseAddress, LocalAddr, PtpFieldAddress, LocalData); #ifdef DEBUG_XAVB_LEVEL3 xil_printf("\r\nWriteToMultipleTxPtpFrames read/mod(PtpBufferPointer = %x) ", PtpBufferPointer); xil_printf("\r\n PTPAddress = %x ", LocalAddr); xil_printf("\r\n PtpFieldAddress = %x ", PtpFieldAddress); xil_printf("\r\n LocalData = %x ", LocalData); #endif } } } /****************************************************************************/ /** * * This function switches the bytes in a 4-byte word, swapping the MSB for the * LSB, and vice-versa. * * @param Data is the 4-byte input data word * * @return The input data word with the bytes swapped (most significant down to * least significant * * @note None. * *****************************************************************************/ u32 XAvb_ReorderWord(u32 Data) { u32 ReOrder = 0; ReOrder = (Data & 0x000000FF) << 24; ReOrder = ReOrder | ((Data & 0x0000FF00) << 8); ReOrder = ReOrder | ((Data & 0x00FF0000) >> 8); ReOrder = ReOrder | ((Data & 0xFF000000) >> 24); return ReOrder; } /****************************************************************************/ /** * * A function to increment the sequenceId in a PTP frame template * * @param BaseAddress is the base address of the device * @param PtpFrameBaseAddress is the base address of the TX PTP Buffer whose * SequenceID is to be incremented * * @return None. But the relevant TX PTP Packet Buffer is written to with the * updated SequenceID * * @note None. * *****************************************************************************/ u32 XAvb_IncSequenceId(u32 BaseAddress, u32 PtpFrameBaseAddress) { u32 BufferWord = 0; u32 SequenceId = 0; /** Read the 32-bit BufferWord containing the SequenceId from the PTP buffer */ BufferWord = XAvb_ReadPtpBuffer(BaseAddress, PtpFrameBaseAddress, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET); /** Swap byte order into correct binary and increment the SequenceId */ SequenceId = XAvb_ReorderWord(BufferWord) + 0x10000; /** Swap back the byte order into frame storage order */ SequenceId = XAvb_ReorderWord(SequenceId); /** Write the 32-bit BufferWord variable containing the updated SequenceId */ XAvb_WritePtpBuffer(BaseAddress, PtpFrameBaseAddress, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET, SequenceId); return SequenceId; } /****************************************************************************/ /** * * The software drivers are kept simple by only requesting a single PTP frame to * be transmitted at a time. This function checks (and if necessary waits) * until the previously request PTP frame has been transmitted. * * @param InstancePtr is a pointer to the XAvb instance to be worked on * * @return None. ** * @note None. * *****************************************************************************/ void XAvb_WaitOnTxPtpQueue(XAvb * InstancePtr) { u32 TxPtpType = 0; do { /** Wait until any queued PTP frame has been transmitted. This is a * software safety feature, not a hardware restriction */ TxPtpType = (XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_CONTROL_OFFSET) & XAVB_PTP_TX_WAIT_ALL_FRAMES_MASK); } while(TxPtpType != 0); } /****************************************************************************/ /** * * A function to format then request the transmission of a PTP Announce Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * * @return None. But the relevant Tx PTP Packet Buffer is written to with the * updated PTP fields, and then the Tx PTP Packet Buffer Control * Register is written to request the frame transmission. * * @note None. * *****************************************************************************/ void XAvb_MasterSendAnnounce(XAvb * InstancePtr) { u32 Unused = 0; /** Wait until there are no PTP frames to be transmitted */ XAvb_WaitOnTxPtpQueue(InstancePtr); /** Increment the sequenceId */ Unused = XAvb_IncSequenceId(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET); #ifdef DEBUG_XAVB_LEVEL3 xil_printf("\r\n--------------------------------"); xil_printf("\r\n---XAvb_MasterSendAnnounce()----"); xil_printf("\r\n--------------------------------"); xil_printf("\r\nsequenceId is %x", Unused); Unused = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_NANOSEC_VALUE_OFFSET); Unused = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_SEC_LOWER_VALUE_OFFSET); xil_printf("\r\nSeconds %d", Unused); #endif /** Send the Announce Frame! */ XAvb_WriteReg(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_CONTROL_OFFSET, XAVB_PTP_TX_SEND_ANNOUNCE_FRAME_MASK); } /****************************************************************************/ /** * * A function to format then request the transmission of a PTP Sync Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * * @return None. But the relevant Tx PTP Packet Buffer is written to with the * updated PTP fields, and then the Tx PTP Packet Buffer Control * Register is written to request the frame transmission. * * @note None. * *****************************************************************************/ void XAvb_MasterSendSync(XAvb * InstancePtr) { u32 Epoch = 0; u32 Sec = 0; u32 SequenceId = 0; u32 BufferWord = 0; /** Wait until there are no PTP frames to be transmitted */ XAvb_WaitOnTxPtpQueue(InstancePtr); /** Increment the sequenceId in the Sync frame */ SequenceId = XAvb_IncSequenceId(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_SYNC_OFFSET); /** Read the current RTC Offset values */ InstancePtr->PtpRecords.Nanosec = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_NANOSEC_VALUE_OFFSET); Sec = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_SEC_LOWER_VALUE_OFFSET); Epoch = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_SEC_UPPER_VALUE_OFFSET); /** Send the Sync Frame! */ XAvb_WriteReg(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_CONTROL_OFFSET, XAVB_PTP_TX_SEND_SYNC_FRAME_MASK); /** Now some pre-work on the Follow-Up Frame *----------------------------------------- * * Write the same sequenceId to the Follow-up frame */ BufferWord = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET); BufferWord = (BufferWord & 0xFFFF0000) | (SequenceId & 0x0000FFFF); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET, BufferWord); /** Format the Timestamp (RTC) into correct byte positioning. * Note: this is how the Timestamp is stored in * the PTP frame itself (transmitted MSB of Epoch first): * * |----------------|----------------|----------------|----------------| * | seconds[23:16] | seconds[31:24] | epoch[7:0] | epoch[15:8] | * |----------------|----------------|----------------|----------------| * | nanosec[23:16] | nanosec[31:24] | seconds[7:0] | seconds[15:8] | * |----------------|----------------|----------------|----------------| * | 0's | 0's | nanosec[7:0] | nanosec[15:8] | * |----------------|----------------|----------------|----------------| */ BufferWord = XAvb_ReorderWord((Epoch<<16) | (Sec>>16)); /** Write the Timestamp (RTC) to the Follow-up frame */ XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_UPPER_OFFSET, BufferWord); BufferWord = XAvb_ReorderWord((Sec<<16) | (InstancePtr->PtpRecords.Nanosec>>16)); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_MID_OFFSET, BufferWord); BufferWord = XAvb_ReorderWord(InstancePtr->PtpRecords.Nanosec<<16); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_LOWER_OFFSET, BufferWord); #ifdef DEBUG_XAVB_LEVEL3 xil_printf("\r\n--------------------------------"); xil_printf("\r\n-----XAvb_MasterSendSync()------"); xil_printf("\r\n--------------------------------"); xil_printf("\r\n--------------RTC---------------"); xil_printf("\r\nRTC nanosec field is %x", InstancePtr->PtpRecords.Nanosec); xil_printf("\r\nRTC seconds field is %x", Sec); xil_printf("\r\nRTC epoch field is %x", Epoch); xil_printf("\r\n--------------------------------"); xil_printf("\r\nsequenceId is %x", SequenceId); #endif } /****************************************************************************/ /** * * A function to format then request the transmission of a PTP Follow-Up Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * * @return None. But the relevant Tx PTP Packet Buffer is written to with the * updated PTP fields, and then the Tx PTP Packet Buffer Control * Register is written to request the frame transmission. * * @note None. * *****************************************************************************/ void XAvb_MasterSendFollowUp(XAvb * InstancePtr) { u32 Timestamp = 0; u32 NsOffset = 0; u32 CorrectionField = 0; u32 BufferWord = 0; /** Wait until there are no PTP frames to be transmitted */ XAvb_WaitOnTxPtpQueue(InstancePtr); /** Read the current RTC offset */ NsOffset = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_NANOSEC_OFFSET); /** Read the Timestamp and adjust it for the MAC transmit latency */ Timestamp = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_SYNC_OFFSET, XAVB_PTP_PKT_CAPTURED_TIMESTAMP_OFFSET) + XAVB_TX_MAC_LATENCY_IN_NS; /** Adjust the Timestamp with current RTC ns offset */ Timestamp = Timestamp + NsOffset; if (Timestamp >= XAVB_ONE_SECOND) { Timestamp = Timestamp - XAVB_ONE_SECOND; } /** Calculate the Correction Field */ CorrectionField = Timestamp - InstancePtr->PtpRecords.Nanosec; if (CorrectionField >= XAVB_ONE_SECOND) { CorrectionField = CorrectionField + XAVB_ONE_SECOND; } /** Format the Correction Field into correct byte positioning for PTP frame * storage in the buffer */ BufferWord = XAvb_ReorderWord(CorrectionField); /** Write the Correction Field to the Follow Up frame */ XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_CORRECTION_FIELD_OFFSET, BufferWord); /** Send the Follow Up Frame! */ XAvb_WriteReg(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_CONTROL_OFFSET, XAVB_PTP_TX_SEND_FOLLOWUP_FRAME_MASK); #ifdef DEBUG_XAVB_LEVEL3 xil_printf("\r\n--------------------------------"); xil_printf("\r\n---XAvb_MasterSendFollowUp()----"); xil_printf("\r\n--------------------------------"); xil_printf("\r\nTimestamp is %x", Timestamp); xil_printf("\r\nRTC nano seconds field is %x", InstancePtr->PtpRecords.Nanosec); xil_printf("\r\nCorrection Field %x", CorrectionField); #endif } /****************************************************************************/ /** * * A function to format then request the transmission of a PTP PDelay Request * Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * * @return None. But the relevant Tx PTP Packet Buffer is written to with the * updated PTP fields, and then the Tx PTP Packet Buffer Control * Register is written to request the frame transmission. * * @note None. * *****************************************************************************/ void XAvb_SendPDelayReq(XAvb * InstancePtr) { u32 SequenceId; /** Wait until there are no PTP frames to be transmitted */ XAvb_WaitOnTxPtpQueue(InstancePtr); /** Increment the SequenceId */ SequenceId = XAvb_IncSequenceId(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYREQ_OFFSET) & 0x0000FFFF; #ifdef DEBUG_XAVB_LEVEL3 xil_printf("\r\n--------------------------------"); xil_printf("\r\n------XAvb_SendPDelayReq()------"); xil_printf("\r\n--------------------------------"); xil_printf("\r\nsequenceId is %x", SequenceId); #endif /** Send the PDelayReq Frame! */ XAvb_WriteReg(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_CONTROL_OFFSET, XAVB_PTP_TX_SEND_PDELAYREQ_FRAME_MASK); /** Wait for the frame to be transmitted */ XAvb_WaitOnTxPtpQueue(InstancePtr); /** Capture the Timestamp for Tx of PDelayReq (t1) and adjust it for MAC * transmit latency */ InstancePtr->PtpRecords.PDelayTimestampT1 = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYREQ_OFFSET, XAVB_PTP_PKT_CAPTURED_TIMESTAMP_OFFSET) + XAVB_TX_MAC_LATENCY_IN_NS; /** Capture the SequenceID of the the PDelayReq */ InstancePtr->SequenceIdRecords.PDelayReqSequenceId = XAvb_ReorderWord(XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYREQ_OFFSET, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET)) >> 16; } /****************************************************************************/ /** * * A function to format then request the transmission of a PTP PDelay Response * Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param PtpFrameBaseAddr is the base address of the received Announce Packet * in the Rx PTP Packet Buffer * * @return None. But the relevant Tx PTP Packet Buffer is written to with the * updated PTP fields, and then the Tx PTP Packet Buffer Control * Register is written to request the frame transmission. * * @note None. * *****************************************************************************/ void XAvb_SendPDelayResp(XAvb * InstancePtr, u32 PtpFrameBaseAddr) { u32 SequenceId = 0; u32 TimestampT2 = 0; u32 BufferWord = 0; u32 NanoSec = 0; u32 Seconds = 0; u32 Epoch = 0; u32 CopyPortId = 0; u32 CopyPortId1 = 0; /** Wait until there are no PTP frames to be transmitted */ XAvb_WaitOnTxPtpQueue(InstancePtr); /** Format the Timestamp *--------------------- * * Capture the current Synchronised time */ NanoSec = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_NANOSEC_VALUE_OFFSET); Seconds = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_SEC_LOWER_VALUE_OFFSET); Epoch = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_SEC_UPPER_VALUE_OFFSET); /** Read the current RTC offset */ InstancePtr->PtpRecords.NsOffsetForPDelayResp = XAvb_ReadReg(InstancePtr->Config.BaseAddress, XAVB_RTC_NANOSEC_OFFSET); /** Read the TimestampT2 for PDelayReq reception and adjust it for MAC * receive latency */ TimestampT2 = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_PKT_CAPTURED_TIMESTAMP_OFFSET) - XAVB_RX_MAC_LATENCY_IN_NS; /** The TimestampT2 was captured using syntonised ns time. We need to * convert this into synchronised time by adding on the current offset */ TimestampT2 = TimestampT2 + InstancePtr->PtpRecords.NsOffsetForPDelayResp; /** Check for ns wrap-around condition */ if (TimestampT2 >= XAVB_ONE_SECOND) { TimestampT2 = TimestampT2 - XAVB_ONE_SECOND; } /** Even though we read the RTC value at the beginning of this * function, there would have been processing delay between the * actual reception (and timestamping) of the PDelayReq frame and the * start of this function. During this time, the RTC Seconds * field could have wrapped around. We need to detect this and if it * has done, the slave Seconds field would also have incremented (so * it needs to be set back). */ if (NanoSec < TimestampT2) { /** NanoSec has wrapped since timestamp was taken so decrement the * Seconds field */ if (Seconds == 0x00000000) { Epoch = Epoch - 0x1; } Seconds = Seconds - 0x1; } /** Format the Timestamp (t2) into correct byte positioning for PTP frame * storage, then write the Timestamp (t2) to the PDelayResp frame */ BufferWord = XAvb_ReorderWord((Epoch<<16) | (Seconds>>16)); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_UPPER_OFFSET, BufferWord); BufferWord = XAvb_ReorderWord((Seconds<<16) | (TimestampT2>>16)); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_MID_OFFSET, BufferWord); BufferWord = XAvb_ReorderWord(TimestampT2<<16); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_LOWER_OFFSET, BufferWord); /** Format the SequenceId *---------------------- * * Set the SequenceId in the PDelayResp and PDelayRespFollowUp frame to be * that of the received PDelayReq frame */ SequenceId = (XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_SEQUENCEID_OFFSET)) & 0x0000FFFF; BufferWord = XAVB_PDELAY_LOG_MEAN_MESSAGE_INT | SequenceId; XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET, BufferWord); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET, BufferWord); /** Format the sourcePortIdentity *------------------------------ * * Copy the sourcePortIdentity field from the PDelayReq into the PDelayResp * and PDelayRespFollowUp frame */ CopyPortId = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_PORTID_UPPER_OFFSET); CopyPortId &= 0xffff0000; CopyPortId1 = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_REQ_PORTID_UPPER_OFFSET); CopyPortId |= (CopyPortId1 & 0x0000ffff); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_REQ_PORTID_UPPER_OFFSET, CopyPortId); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_REQ_PORTID_UPPER_OFFSET, CopyPortId); CopyPortId = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_PORTID_MID_OFFSET); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_REQ_PORTID_MID_OFFSET, CopyPortId); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_REQ_PORTID_MID_OFFSET, CopyPortId); CopyPortId = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_PORTID_LOWER_OFFSET); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_TX_PKT_REQ_PORTID_LOWER_OFFSET, CopyPortId); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_REQ_PORTID_LOWER_OFFSET, CopyPortId); /** Send the PDelayResp Frame! *---------------------------*/ XAvb_WriteReg(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_CONTROL_OFFSET, XAVB_PTP_TX_SEND_PDELAYRESP_FRAME_MASK); #ifdef DEBUG_XAVB_LEVEL3 xil_printf("\r\n--------------------------------"); xil_printf("\r\n------XAvb_SendPDelayResp()-----"); xil_printf("\r\n--------------------------------"); xil_printf("\r\nTimestampT2 is %x", TimestampT2); #endif } /****************************************************************************/ /** * * A function to format then request the transmission of a PTP PDelay Response * Follow-Up Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * * @return None. But the relevant Tx PTP Packet Buffer is written to with the * updated PTP fields, and then the Tx PTP Packet Buffer Control * Register is written to request the frame transmission. * * @note None. * *****************************************************************************/ void XAvb_SendPDelayRespFollowUp(XAvb * InstancePtr) { u32 TimestampT3 = 0; u32 BufferWordA = 0; u32 BufferWordB = 0; XAvb_RtcFormat Rtc[1]; /** Wait until there are no PTP frames to be transmitted */ XAvb_WaitOnTxPtpQueue(InstancePtr); /** Format the Timestamp *---------------------*/ /** Capture the current Synchronised time */ XAvb_ReadRtc(InstancePtr->Config.BaseAddress, Rtc); /** Read the TimestampT3 for PDelayResp transmission and adjust it for MAC * transmit latency */ TimestampT3 = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_OFFSET, XAVB_PTP_PKT_CAPTURED_TIMESTAMP_OFFSET) + XAVB_TX_MAC_LATENCY_IN_NS; /** The TimestampT3 was captured using syntonised ns time. We need to * convert this into synchronised time by adding on the ns offset. We * use the same offset here as for the PDelayResp frame since if a * ns offset change had been made between PDelayResp and , * PDelayRespFollowUp, this would result in an error in the link delay * measurement. */ TimestampT3 = TimestampT3 + InstancePtr->PtpRecords.NsOffsetForPDelayResp; /** Check for ns wrap-around condition */ if (TimestampT3 >= XAVB_ONE_SECOND) { TimestampT3 = TimestampT3 - XAVB_ONE_SECOND; } /** Even though we read the RTC value at the beginning of this * function, there would have been processing delay between the * actual reception (and timestamping) of the PDelayReq frame and the * start of this function. During this time, the RTC seconds * field could have wrapped around. We need to detect this and if it * has done, the slave seconds field would also have incremented (so * it needs to be set back). */ if (Rtc->NanoSeconds < TimestampT3) { /** nanosec has wrapped since timestamp was taken so decrement the * seconds field */ if (Rtc->SecondsLower == 0x00000000) { Rtc->SecondsUpper = Rtc->SecondsUpper - 0x1; } Rtc->SecondsLower = Rtc->SecondsLower - 0x1; } /** Format the Timestamp (t3) into correct byte positioning for PTP frame * storage, the write the Timestamp (t3) to the PDelayRespFollowUp frame */ BufferWordA = XAvb_ReorderWord((Rtc->SecondsUpper<<16) | (Rtc->SecondsLower>>16)); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_UPPER_OFFSET, BufferWordA); BufferWordA = XAvb_ReorderWord((Rtc->SecondsLower<<16) | (TimestampT3>>16)); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_MID_OFFSET, BufferWordA); BufferWordA = XAvb_ReorderWord(TimestampT3<<16); BufferWordB = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_LOWER_OFFSET); BufferWordA &= 0x0000ffff; BufferWordA |= (BufferWordB & 0xffff0000); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYRESP_FOLLOW_UP_OFFSET, XAVB_PTP_TX_PKT_TIMESTAMP_LOWER_OFFSET, BufferWordA); /** Send the PDelayRespFollowUp Frame! *----------------------------------- */ XAvb_WriteReg(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_CONTROL_OFFSET, XAVB_PTP_TX_SEND_PDELAYRESPFOLLOWUP_FRAME_MASK); #ifdef DEBUG_XAVB_LEVEL3 xil_printf("\r\n--------------------------------"); xil_printf("\r\n--XAvb_SendPDelayRespFollowUp()-"); xil_printf("\r\n--------------------------------"); xil_printf("\r\nReading TimestampT3 from address %x", (InstancePtr->Config.BaseAddress + XAVB_PTP_TX_PDELAYRESP_OFFSET + XAVB_PTP_PKT_CAPTURED_TIMESTAMP_OFFSET)); xil_printf("\r\nTimestampT3 is %x", TimestampT3); #endif } /****************************************************************************/ /** * * A function to check that various fields in the received frame contain the * expected values which define it as a valid AVB PTP frame. If this check does * not pass then the frame should not be decoded and used. * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param PtpFrameBaseAddr is the base address of the received Announce Packet * in the Rx PTP Packet Buffer * * @return An updated True/False decision as to whether this received frame * really is a valid PTP type. * * @note None. * *****************************************************************************/ u32 XAvb_IsRxFramePTP(XAvb * InstancePtr, u32 PtpFrameBaseAddr) { u32 FrameIsPTP; u32 FrameField; /** Start by assuming that it is a valid PTP frame */ FrameIsPTP = 1; /** Perform a 32-bit read from the relevant position in the frame */ FrameField = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_TYPE_OFFSET); FrameField = XAvb_ReorderWord(FrameField); /** Check the Length/Type field for a valid Ethertype */ if ((FrameField >>16) != XAVB_PTP_ETHERTYPE) { FrameIsPTP = 0; #ifdef DEBUG_XAVB_LEVEL1 xil_printf("\r\nXAvb_IsRxFramePTP(): Bad Ethertype: %x", (FrameField >>16)); #endif } /** Check the versionPTP */ if ((FrameField & 0xF) != XAVB_PTP_VERSION_PTP) { FrameIsPTP = 0; #ifdef DEBUG_XAVB_LEVEL1 xil_printf("\r\nXAvb_IsRxFramePTP():Bad versionPTP:%x", (FrameField & 0xF)); #endif } return FrameIsPTP; } /****************************************************************************/ /** * * A function to decode a received PTP Sync Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param PtpFrameBaseAddr is the base address of the received Announce Packet * in the Rx PTP Packet Buffer * * @return None. * * @note None. * *****************************************************************************/ void XAvb_DecodeRxSync(XAvb * InstancePtr, u32 PtpFrameBaseAddr) { u32 ReadWord = 0; XAvb_PortIdentity syncPortID; /** Read sourcePortIdentity from packet */ XAvb_GetPortIdentity(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_PORTID_UPPER_OFFSET, &syncPortID); /** Only decode if configured for a slave and if SourcePortID is that of the * RTC Clock Master */ if ( (InstancePtr->CurrentBmc.IAmTheRtcMaster == 0) && XAvb_ComparePortIdentity(InstancePtr->Config.BaseAddress, InstancePtr->CurrentBmc.SourcePortIdentity, syncPortID) ) { /** Reset Sync Interval Counter as we have received a valid Sync */ InstancePtr->PtpCounters.CounterSyncInterval = 0; /** Capture the local Timestamp for receipt of this frame and adjust it for * MAC receive latency */ InstancePtr->PtpRecords.SlaveSyncTimestamp = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_PKT_CAPTURED_TIMESTAMP_OFFSET) - XAVB_RX_MAC_LATENCY_IN_NS; /** Capture the Sync SequenceID */ ReadWord = XAvb_ReorderWord(XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_SEQUENCEID_OFFSET)); InstancePtr->SequenceIdRecords.SyncSequenceId = (ReadWord >> 16); /** Capture the logMeanMessageInterval and convert into a useful duration * (NOTE: there is an implicit conversion from u32 to char here) */ InstancePtr->latestMDSyncReceive.logMessageInterval = ReadWord & 0x000000ff; InstancePtr->latestMDSyncReceive.SyncIntervalDuration = XAvb_ConvertLogMeanToDuration(InstancePtr->latestMDSyncReceive.logMessageInterval); /** We don't need to capture the correction field - unless we want to check that it is 0.*/ } else { xil_printf("\r\nXAvb_DecodeRxSync()"); xil_printf("\r\nSync ignored due to unmatched SourcePortID"); #ifdef DEBUG_XAVB_LEVEL1 xil_printf("\r\nInstancePtr->CurrentBmc.SourcePortIdentity.ClockIdentityUpper = %x",InstancePtr->CurrentBmc.SourcePortIdentity.ClockIdentityUpper); xil_printf("\r\nInstancePtr->CurrentBmc.SourcePortIdentity.ClockIdentityLower = %x",InstancePtr->CurrentBmc.SourcePortIdentity.ClockIdentityLower); xil_printf("\r\nInstancePtr->CurrentBmc.SourcePortIdentity.PortNumber = %x\r\n",InstancePtr->CurrentBmc.SourcePortIdentity.PortNumber); xil_printf("\r\nsyncPortID.ClockIdentityUpper = %x",syncPortID.ClockIdentityUpper); xil_printf("\r\nsyncPortID.ClockIdentityLower = %x",syncPortID.ClockIdentityLower); xil_printf("\r\nsyncPortID.PortNumber = %x\r\n",syncPortID.PortNumber); #endif } } /****************************************************************************/ /** * * A function to decode a received PTP Follow-up Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param PtpFrameBaseAddr is the base address of the received Announce Packet * in the Rx PTP Packet Buffer * * @return None. * * @note None. * *****************************************************************************/ void XAvb_DecodeRxFollowUp(XAvb * InstancePtr, u32 PtpFrameBaseAddr) { XAvb_PortIdentity followUpPortID; /** Read sourcePortIdentity from packet */ XAvb_GetPortIdentity(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_PORTID_UPPER_OFFSET, &followUpPortID); /** Only decode if configured for a slave and if SA is that of the RTC * Clock Master */ if ( (InstancePtr->CurrentBmc.IAmTheRtcMaster == 0) && XAvb_ComparePortIdentity(InstancePtr->Config.BaseAddress, InstancePtr->CurrentBmc.SourcePortIdentity, followUpPortID) ) { /** Capture the Follow Up SequenceID */ InstancePtr->SequenceIdRecords.FollowUpSequenceId = XAvb_ReorderWord(XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_SEQUENCEID_OFFSET)) >> 16; /** SequenceID in Follow Up Frame should always match that of the * Sync Frame */ if (InstancePtr->SequenceIdRecords.FollowUpSequenceId == InstancePtr->SequenceIdRecords.SyncSequenceId) { /** Capture the correction field from follow up frame */ InstancePtr->PtpRecords.MasterCorrectionField = XAvb_ReorderWord(XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_CORRECTION_FIELD_OFFSET)); /** Perform the Course RTC Offset correction for every Sync / * FollowUp pair */ XAvb_CalcRtcOffset(InstancePtr, PtpFrameBaseAddr); /** Every n Sync / FollowUp pairs, we are going to calculate a * corrected increment rate of RTC */ if ((InstancePtr->PtpCounters.CounterSyncEvents & 0xF) == (XAVB_NUM_SYNC_FU_PAIR_CALC_RTC_INCREMENT - 1)) { /** Reset the CounterSyncEvents Counter */ InstancePtr->PtpCounters.CounterSyncEvents = 0x0; /** Capture the Sequence ID of the Follow Up frame */ InstancePtr->SequenceIdRecords.NewSyncSequenceId = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_SEQUENCEID_OFFSET); InstancePtr->SequenceIdRecords.NewSyncSequenceId = (XAvb_ReorderWord (InstancePtr->SequenceIdRecords.NewSyncSequenceId) ) >> 16; /** Perform the RTC increment rate adjustment calculation */ XAvb_UpdateRtcIncrement(InstancePtr); /** Sample Sync Frame Time sent (as estimated by the slave) for * comparison in ten more repetition's time */ InstancePtr->PtpRecords.OldSlaveTime = InstancePtr->PtpRecords.NewSlaveTime; /** Sample Sync Frame Time sent (as calculated by the master) * for comparison in ten more repetition's time */ InstancePtr->PtpRecords.OldMasterTime = InstancePtr->PtpRecords.NewMasterTime; /** Sample the current Follow Up Sequence ID for comparison in * ten more repetition's time */ InstancePtr->SequenceIdRecords.OldSyncSequenceId = InstancePtr->SequenceIdRecords.NewSyncSequenceId; } else { InstancePtr->PtpCounters.CounterSyncEvents = InstancePtr->PtpCounters.CounterSyncEvents + 1; } } else { xil_printf("SequenceIDs on RxFollowup don't match.\r\n"); } } else { xil_printf("\r\nXAvb_DecodeRxFollowUp()"); xil_printf("\r\nFollowUp ignored due to unmatched SourcePortID\r\n"); } } /****************************************************************************/ /** * * A function to decode a received PDelayResp Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param PtpFrameBaseAddr is the base address of the received Announce Packet * in the Rx PTP Packet Buffer * * @return None. * * @note None. * *****************************************************************************/ void XAvb_DecodeRxPDelayResp(XAvb * InstancePtr, u32 PtpFrameBaseAddr) { /** Have we already seen a PDelayResp since the last * PDelayReq was sent? If so, ignore the packet */ if( InstancePtr->StateMachineData.rcvdPDelayResp ) { xil_printf("Error: already saw a PDelayResp since the last PDelayReq was sent\r\n"); return; } /** Find the ClockIdentity of the Sender */ XAvb_GetPortIdentity(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_PORTID_UPPER_OFFSET, &InstancePtr->StateMachineData.respPortIdentity); /** Is the PDelayResp message from ourself? If so, the Peer * is most likely a dumb hub and should be considered not * ASCapable */ if( XAvb_ComparePortIdentity(InstancePtr->Config.BaseAddress, InstancePtr->portIdLocal, InstancePtr->StateMachineData.respPortIdentity) ) { XAvb_ChangePeerASCapability(InstancePtr, 0); xil_printf("\r\nXAvb_DecodeRxPDelayResp():The peer is no longer ASCapable "); xil_printf("\r\nXAvb_DecodeRxPDelayResp():Saw a PDelayResp from myself\r\n"); return; } /** Capture the requestingPortIdentity */ XAvb_GetPortIdentity(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_REQ_PORTID_UPPER_OFFSET, &InstancePtr->StateMachineData.respReqPortIdentity); /** Capture the PDelayResp SequenceID */ InstancePtr->SequenceIdRecords.PDelayRespSequenceId = XAvb_ReorderWord(XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_SEQUENCEID_OFFSET)) >> 16; /** Verify that the requestingPortIdentity matches our * portIdentity */ if( !XAvb_ComparePortIdentity(InstancePtr->Config.BaseAddress, InstancePtr->portIdLocal, InstancePtr->StateMachineData.respReqPortIdentity) ) { xil_printf("Error: PDelayResp reqPortID doesn't match our portID\r\n"); return; } /** Only process if the received frame's sequenceId matches * the sequenceId sent in the last pDelay_Req packet */ if( (InstancePtr->SequenceIdRecords.PDelayReqSequenceId == InstancePtr->SequenceIdRecords.PDelayRespSequenceId) ) { /** Mark this as a valid PDelayResp packet */ InstancePtr->StateMachineData.rcvdPDelayResp = 1; /** Capture timestamp for receipt time of PDelayReq at Master (t2) */ InstancePtr->PtpRecords.PDelayTimestampT2 = XAvb_CaptureNanoSec(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr); /** Capture timestamp for receipt time of PDelayResp at Slave (t4) and adjust * it for MAC receive latency */ InstancePtr->PtpRecords.PDelayTimestampT4 = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_PKT_CAPTURED_TIMESTAMP_OFFSET) - XAVB_RX_MAC_LATENCY_IN_NS; } else { xil_printf("Error: PDelayResp seqID's don't match\r\n"); } } /****************************************************************************/ /** * * A function to decode a received PDelayRespFollowUp Packet * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param PtpFrameBaseAddr is the base address of the received Announce Packet * in the Rx PTP Packet Buffer * * @return None. * * @note None. * *****************************************************************************/ void XAvb_DecodeRxPDelayRespFollowUp(XAvb * InstancePtr, u32 PtpFrameBaseAddr) { XAvb_PortIdentity portId; /** Has a valid PDelayResp packet been received since the * last PDelayReq packet was sent? */ if( !InstancePtr->StateMachineData.rcvdPDelayResp ) { /*xil_printf("Error: Received a PDelayRespFollowUp before receiving a PDelayResp\r\n");*/ return; } /** Capture the PDelayRespFollowUp SequenceID */ InstancePtr->SequenceIdRecords.PDelayFollowUpSequenceId = XAvb_ReorderWord(XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_SEQUENCEID_OFFSET)) >> 16; /** Get the sourcePortIdentity of the sender */ XAvb_GetPortIdentity(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_PORTID_UPPER_OFFSET, &portId); /** The sourcePortIdentity of the PDelayRespFollowUp should * match that of the last PDelayResp packet received */ if( !XAvb_ComparePortIdentity(InstancePtr->Config.BaseAddress, portId, InstancePtr->StateMachineData.respPortIdentity) ) { xil_printf("Error: sourcePortIdentity of PDelayRespFollowUp doesn't match PDelayResp\r\n"); return; } /** Get the requestingPortIdentity of the sender */ XAvb_GetPortIdentity(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_REQ_PORTID_UPPER_OFFSET, &portId); /** The requestingPortIdentity of the PDelayRespFollowUp should * match that of the last PDelayResp packet received */ if( !XAvb_ComparePortIdentity(InstancePtr->Config.BaseAddress, portId, InstancePtr->StateMachineData.respReqPortIdentity) ) { xil_printf("Error: reqPortID of PDelayRespFollowUp doesn't match PDelayResp\r\n"); return; } /** SequenceID of PDelayRespFollowUp Frame should always match that of * the PDelayResp Frame and the original PDelayReq Frame. */ if (InstancePtr->SequenceIdRecords.PDelayFollowUpSequenceId == InstancePtr->SequenceIdRecords.PDelayRespSequenceId) { /** Mark this as a valid PDelayRespFollowUp packet */ InstancePtr->StateMachineData.rcvdPDelayRespFollowUp = 1; /** Capture the timestamp for transmit time of PDelayResp at Master * (t3) */ InstancePtr->PtpRecords.PDelayTimestampT3 = XAvb_CaptureNanoSec(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr); /** Now we know t1, t2, t3 and t4, calculate the link delay */ XAvb_CalcDelay(InstancePtr); } else { xil_printf("Error: seqID of PDelayRespFollowUp doesn't match PDelayResp\r\n"); } } /****************************************************************************/ /** * * A function to decode a received Signalling Packet and modify the TX PTP * Buffers based on the requested values. * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param PtpFrameBaseAddr is the base address of the received Signaling Packet * in the Rx PTP Packet Buffer * * @return None. * * @note None. * *****************************************************************************/ void XAvb_DecodeRxSignaling(XAvb * InstancePtr, u32 PtpFrameBaseAddr) { u32 ReadData; char currentInterval; /** Read the requested logMeanMessage durations from the Signalling frame */ ReadData = XAvb_ReorderWord(XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, PtpFrameBaseAddr, XAVB_PTP_RX_PKT_SIGNALING_DELAY_INTERVAL_OFFSET)); /** update linkDelayInterval */ currentInterval = (ReadData >> 24); switch( currentInterval ) { case (-128): /** don't change the interval */ break; /** currently only support the default value */ case XAVB_DEFAULT_LOG_MEAN_PDELAY_REQ_INTERVAL: case 126: /** set the interval to initial value */ InstancePtr->SignallingFrameData.LinkDelayIntervalDuration = XAvb_UpdateIntervalDuration(InstancePtr->SignallingFrameData.LinkDelayIntervalDuration, XAVB_DEFAULT_LOG_MEAN_PDELAY_REQ_INTERVAL); /** Update logMeanMessageInterval in the pre-loaded TX PDELAYREQ message buffer */ XAvb_UpdateLogMeanMessageInterval(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PDELAYREQ_OFFSET, InstancePtr->SignallingFrameData.LinkDelayIntervalDuration); break; case 127: /** stop sending pDelay messages */ InstancePtr->SignallingFrameData.LinkDelayIntervalDuration = XAVB_PKT_TYPE_DISABLED; break; default: xil_printf( "Got a signalling message with an interval (%d) I don't support!\r\n", currentInterval); } /** update timeSyncInterval */ currentInterval = (ReadData >> 16); switch( currentInterval ) { case (-128): /** don't change the interval */ break; case XAVB_DEFAULT_LOG_MEAN_SYNC_INTERVAL: case 126: /** set the interval to initial value */ InstancePtr->SignallingFrameData.SyncIntervalDuration = XAvb_UpdateIntervalDuration(InstancePtr->SignallingFrameData.SyncIntervalDuration, XAVB_DEFAULT_LOG_MEAN_SYNC_INTERVAL); /** Update logMeanMessageInterval in the pre-loaded TX SYNC message buffer */ XAvb_UpdateLogMeanMessageInterval(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_SYNC_OFFSET, InstancePtr->SignallingFrameData.SyncIntervalDuration); /** Update logMeanMessageInterval in the pre-loaded TX FOLLOW_UP message buffer */ XAvb_UpdateLogMeanMessageInterval(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_FOLLOW_UP_OFFSET, InstancePtr->SignallingFrameData.SyncIntervalDuration); break; case 127: /** stop sending sync messages */ InstancePtr->SignallingFrameData.SyncIntervalDuration = XAVB_PKT_TYPE_DISABLED; break; default: xil_printf( "Got a signalling message with an interval (%d) I don't support!\r\n", currentInterval); } /** update announceInterval */ currentInterval = (ReadData >> 8); switch( currentInterval ) { case (-128): /** don't change the interval */ break; case XAVB_DEFAULT_LOG_MEAN_ANNOUNCE_INTERVAL: case 126: /** set the interval to initial value */ InstancePtr->SignallingFrameData.AnnounceIntervalDuration = XAvb_UpdateIntervalDuration(InstancePtr->SignallingFrameData.AnnounceIntervalDuration, XAVB_DEFAULT_LOG_MEAN_ANNOUNCE_INTERVAL); /** Update logMeanMessageInterval in the pre-loaded TX ANNOUNCE message buffer */ XAvb_UpdateLogMeanMessageInterval(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, InstancePtr->SignallingFrameData.AnnounceIntervalDuration); break; case 127: /** stop sending Announce messages */ InstancePtr->SignallingFrameData.AnnounceIntervalDuration = XAVB_PKT_TYPE_DISABLED; break; default: xil_printf( "Got a signalling message with an interval (%d) I don't support!\r\n", currentInterval); } } /****************************************************************************/ /** * * A function to update a PTP message Interval Duration (defined as a * fraction of 128 seconds). If the endpoint cannot support a requested * logMeanVal then do not perform the conversion - return the current value. * * @param currentIntervalDuration is the Interval Duration to be updated * @param logMeanVal is the base2 value that is to be converted * * @return logMeanVal represented as a fraction of 128 * * @note This endpoint only supports logMeanValues >=-7 and <=8. * *****************************************************************************/ u16 XAvb_UpdateIntervalDuration(u16 currentIntervalDuration, char logMeanVal) { if((logMeanVal >= XAVB_MIN_SUPPORTED_LOG_MEAN_INTERVAL) && (logMeanVal <= XAVB_MAX_SUPPORTED_LOG_MEAN_INTERVAL)) { return XAvb_ConvertLogMeanToDuration(logMeanVal); } else { return currentIntervalDuration; } } /****************************************************************************/ /** * * A function to convert a logMean (power of 2) value into a fraction of 128 * that is compatible with Signalling data. * * @param logMeanVal is the base2 value that is to be converted * @return logMeanVal represented as a fraction of 128 * * @note None. * *****************************************************************************/ u16 XAvb_ConvertLogMeanToDuration(char logMeanVal) { u8 logMeanAbs; logMeanAbs = (u8)(abs(logMeanVal)); return (logMeanVal < 0) ? (128 >> logMeanAbs) : (logMeanVal > 0) ? (128 << logMeanAbs) : 128; } /****************************************************************************/ /** * * A function to convert a fraction of 128 value that is compatible with * Signalling data into a logMean (power of 2) value; * * @param fractionalVal is the Signalling data value that is to be converted * @return fractionalVal represented as logMean (power of 2) value * * @note None. * *****************************************************************************/ char XAvb_ConvertDurationToLogMean(u16 fractionalVal) { char logMeanVal = 0; u8 numShifts = 0; /** just in case fractionalVal is not a power of 2, we'll * only look at the most significant bit * Count how many shifts it takes for most significant set bit * to be in the highest (16th) bit location */ while( !(fractionalVal & 0x8000) ) { fractionalVal <<= 1; numShifts++; } /** logMeanVal = 0 = 2^0 = 128/128 would give us a numShifts * result of 8, so 8 will be our base */ logMeanVal = 8 - numShifts; return logMeanVal; } /****************************************************************************/ /** * * A function to update the logMeanMessageInterval field in a PTP packet * * @param BaseAddress is the base address of the device * @param PtpFrameBaseAddr is the base address of the TX PTP Buffer to be updated * @param intervalDuration is the "fraction of 128" value of the data to be written * * @return None. But the relevant Tx PTP Packet Buffer is written to with the * updated logMeanMessageInterval * * @note None. * *****************************************************************************/ void XAvb_UpdateLogMeanMessageInterval(u32 BaseAddress, u32 PtpFrameBaseAddr, u16 intervalDuration) { u32 ReadVal; u8 logMean; /** Convert intervalDuration to a logMean value */ logMean = (u8)XAvb_ConvertDurationToLogMean(intervalDuration); /** Read the current fields */ ReadVal = XAvb_ReadPtpBuffer(BaseAddress, PtpFrameBaseAddr, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET); /** Update just the logMeanMessageInterval field */ ReadVal = (ReadVal & 0x00ffffff) | (logMean << 24); /** Write back */ XAvb_WritePtpBuffer(BaseAddress, PtpFrameBaseAddr, XAVB_PTP_TX_PKT_SEQUENCEID_OFFSET, ReadVal); } /****************************************************************************/ /** * * This function updates the portIdLocal local copy of the sourcePortIdentity * and writes this value into the TX PTP frame buffer templates. The fields * that are written are: * o sourcePortIdentity for all default PTP frames * o Announce:: grandmasterIdentity * o Announce:: TLV clockIdentity * * @param InstancePtr is a pointer to the XAvb instance to be worked on * @param systemIdentity is the clockIdentity and portNumber for this endpoint * * @return None. * * @note Announce::TLV. By default the tlvType and length field are set * up in the BRAM, assuming that N = 1. * ****************************************************************************/ void XAvb_SetupSourcePortIdentity(XAvb * InstancePtr, XAvb_PortIdentity systemIdentity) { u32 BufferWord; u8 BufferEnable; u32 DataBitEnable; u32 ReadWord; /* Set up our local copy of the sourcePortIdentity */ InstancePtr->portIdLocal = systemIdentity; /** Write the sourcePortIdentity into the header for all TX PTP buffers * except the empty default buffer AND write the GMID for TX announce AND * Write the ClockIdentity into the TX Announce TLV */ BufferEnable = 0x7F; /** (a) Write the upper 2 bytes of the ClockIdentityUpper * - REM: Swap back the byte order into frame storage order */ DataBitEnable = 0xFFFF0000; BufferWord = (systemIdentity.ClockIdentityUpper >> 16); BufferWord = XAvb_ReorderWord(BufferWord); XAvb_WriteToMultipleTxPtpFrames(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PKT_PORTID_UPPER_OFFSET, BufferWord, DataBitEnable, BufferEnable); ReadWord = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, XAVB_PTP_TX_PKT_ANNOUNCE_TLVLEN_PATHSEQ_START_OFFSET); BufferWord = (ReadWord & 0x0000FFFF) | (BufferWord & 0xFFFF0000); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, XAVB_PTP_TX_PKT_ANNOUNCE_TLVLEN_PATHSEQ_START_OFFSET, BufferWord); /** (b) Write the lower 2 bytes of the ClockIdentityUpper and upper * 2 bytes of the ClockIdentityLower. * - REM: Swap back the byte order into frame storage order*/ BufferWord = ((systemIdentity.ClockIdentityUpper << 16) & 0xFFFF0000) | ((systemIdentity.ClockIdentityLower >> 16) & 0x0000FFFF); BufferWord = XAvb_ReorderWord(BufferWord); DataBitEnable = 0xFFFFFFFF; XAvb_WriteToMultipleTxPtpFrames(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PKT_PORTID_MID_OFFSET, BufferWord, DataBitEnable, BufferEnable); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, (XAVB_PTP_TX_PKT_ANNOUNCE_TLVLEN_PATHSEQ_START_OFFSET + 4), BufferWord); /** (c) Write the lower 2 bytes of the ClockIdentityLower and the portNumber * - REM: Swap back the byte order into frame storage order*/ BufferWord = ((systemIdentity.ClockIdentityLower << 16) & 0xFFFF0000) | (systemIdentity.PortNumber & 0x0000FFFF); BufferWord = XAvb_ReorderWord(BufferWord); XAvb_WriteToMultipleTxPtpFrames(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_PKT_PORTID_LOWER_OFFSET, BufferWord, DataBitEnable, BufferEnable); BufferWord = (systemIdentity.ClockIdentityLower << 16) & 0xFFFF0000; BufferWord = XAvb_ReorderWord(BufferWord); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, (XAVB_PTP_TX_PKT_ANNOUNCE_TLVLEN_PATHSEQ_START_OFFSET + 8), BufferWord); /** Write the grandmasterIdentity into the header for the TX Announce PTP buffer */ /** (a) Write 1 byte of GMID (Upper)*/ BufferWord = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, XAVB_PTP_TX_PKT_ANNOUNCE_QUAL_LOW_PRI2_GMID_HI_OFFSET); BufferWord = (BufferWord & 0x00FFFFFF) | (systemIdentity.ClockIdentityUpper & 0xFF000000); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, XAVB_PTP_TX_PKT_ANNOUNCE_QUAL_LOW_PRI2_GMID_HI_OFFSET, BufferWord); /** (b) Write 3 bytes of GMID (Upper) and 1 byte of GMID (Lower)**/ BufferWord = (systemIdentity.ClockIdentityUpper << 8) | ((systemIdentity.ClockIdentityLower >> 24) & 0x000000FF); BufferWord = XAvb_ReorderWord(BufferWord); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, XAVB_PTP_TX_PKT_ANNOUNCE_GMID_MID_OFFSET, BufferWord); /** (c) Write 3 bytes of GMID (Lower) */ BufferWord = XAvb_ReadPtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, XAVB_PTP_TX_PKT_ANNOUNCE_GMID_LOW_STEPSREMOVED_HI_OFFSET); BufferWord = ((BufferWord >> 24) & 0x000000FF) | ((systemIdentity.ClockIdentityLower << 8) & 0xFFFFFF00); BufferWord = XAvb_ReorderWord(BufferWord); XAvb_WritePtpBuffer(InstancePtr->Config.BaseAddress, XAVB_PTP_TX_ANNOUNCE_OFFSET, XAVB_PTP_TX_PKT_ANNOUNCE_GMID_LOW_STEPSREMOVED_HI_OFFSET, BufferWord); #ifdef DEBUG_XAVB_LEVEL2 xil_printf("\r\n setupSourcePortIdentity: Writing systemIdentity to buffers %08x", BufferEnable); xil_printf("\r\n setupSourcePortIdentity: ClockIdentityUpper --> 0x%08x, ClockIdentityLower --> 0x%08x, PortNumber --> 0x%08x", InstancePtr->portIdLocal.ClockIdentityUpper, InstancePtr->portIdLocal.ClockIdentityLower, InstancePtr->portIdLocal.PortNumber); #endif }