This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP432E401Y: PTP timestamping

Part Number: MSP432E401Y


Hi,

I have been working on a PTP solution for the the MSP432 Launchpad dev board. Things are starting to work but I still have a few issues.

So far:

I have a Trimble Thunderbolt GM200 PTP source connected to my Launchpad via a Netgear GS108E managed switch.

This allows me to set up a monitor port so I can see the traffic on Wireshark on a PC.

To set up PTP timestamping, I added a Net Start callback function in the config GUI (not sure if this is the best place but it seems to work):

void netStartHook()
{
    Display_printf(display, 0, 0, "Network Start hook: ");

    uint32_t ui32Config;
    uint32_t ui32SubSecondInc = 40;

    ui32Config = EMAC_TS_PTP_VERSION_2 | EMAC_TS_DIGITAL_ROLLOVER | EMAC_TS_MAC_FILTER_DISABLE | EMAC_TS_UPDATE_COARSE | EMAC_TS_ALL | EMAC_TS_PROCESS_IPV4_UDP;

    EMACTimestampConfigSet(EMAC0_BASE, ui32Config, ui32SubSecondInc);

    EMACTimestampEnable(EMAC0_BASE);
}

I have joined the PTP multicast, receive the PTP sync messages every second, and respond to every other (arbitrary choice) SYNC with a DELAY_REQ message. The PTP server responds to the DELAY_REQ with a DELAY_RESP message.

All of these messages show up on Wirsehark as expected.

I have found the Tx and RX DMA descriptor blocks in the debugger and can see them updating as PTP messages come and go - see screenshot. The timestamps seconds and nanoseconds are in the 2nd and 3rd column from the right.

Problems:

1) The Tx descriptor timestamps are not getting updated whereas the Rx descriptor timestamps appear correct. I don't know if I need to do something else to request Tx message timestamps, or if my messages are faulty and don't look like proper PTP messages - Wireshark shows them as PTPv2.

2) I have found the DMA descriptors in the debugger and I assume I can get their addresses programmatically via the EMACRxDMADescriptorListGet() and EMACTxDMADescriptorListGet() functions.
There is an array of 4 descriptors each for Tx and Rx. As my network currently has very few messages, I can see the updates cycling through them, via the debugger.
I am not sure how I know which descriptor to read, to get the timestamp for the message I have just received or transmitted. I could read all 4 and take the highest timestamp, but I hope there might be some better way.

TIA

Jim

  • Hi Jim,

      As mentioned by Jace Hall in this post to you, we do not have any support for PTP from TI-RTOS NDK and neither do we have any software examples. Sorry for the lack of guidance. 

    https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1121643/msp432e401y-ptp-support?tisearch=e2e-sitesearch&keymatch=PTP#

    As far as EMACTxDMADescriptorListGet(), it should return a pointer to the head of the EMAC's transmit DMA descriptor list. 

  • Hi,

    Yes, I realised from the previous thread that I was going to have to do this myself and it is looking promising but I do have some problems as described above. 
    You do state PTP support on page 1 of the product datasheet. The hardware is clearly present so there must be someone there that knows something about it!

    Could you please involve an EMAC hardware expert to suggest what I may be doing wrong regarding Tx timestamping.

    I would also appreciate input from an EMAC driver expert who may be able to suggest how I can determine the appropriate DMA descriptor, relevant to the packet I have just sent or received.

    Best regards

    Jim

  • Hi Jim,

    You do state PTP support on page 1 of the product datasheet. The hardware is clearly present so there must be someone there that knows something about it!

    Yes, the EMAC hardware does support timestamping. Sorry, It is just that we don't have anyone with experience in PTP. I have done some searches and come across this post. Perhaps it will be useful to you as the poster shares some example at the end using TivaWare. 

    https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/481631/tm4c129-and-emac-ieee-1588-timestamp-read-precision/1735347?tisearch=e2e-sitesearch&keymatch=ptp#1735347

  • Hi,
    I think I have a sorted most of the PTP issues.
    Although the IEEE 1588 spec is a bit painful, the bottom line is really fairly simple - Wait for a message, reply, wait for a reply to that, extract some timestamps, do some simple arithmetic, adjust your clock, job done!

    My problems are more related to the MSP432 EMAC and device driver.

    Firstly, the EMAC appears to be timestamping received messages but not transmitted messages. Perhaps an EMAC expert would be able to suggest what I might be doing wrong!

    Secondly, the DMA copies the Rx timestamps (and presumably Tx timestamps if I can get that working) into a DMA descriptor (32 bytes of memory). It cycles round 4 DMA descriptors and I would appreciate any help possible in working out how to determine which descriptor was used for the message I have just sent or received. Perhaps a device driver expert could shed some light on this.

    If I can fix these 2 problems, then I believe I have a working PTP system.

    Best regards
    Jim Whitehouse

  • Hi Jim,

    Looking at your TX descriptor, the bit25 is clear meaning the timestamp is not enabled for some reason. 

    With advanced timestamp support, the snapshot of the timestamp to be taken can be enabled for
    a given frame by setting Bit 25 (TTSE) of TDES0. When the descriptor is closed (that is, when the
    OWN bit is cleared), the timestamp is written into TDES6 and TDES7.
    Note: When the Advanced Timestamp feature is enabled, software should set the ATDS bit of the
    Ethernet MAC DMA Bus Mode (EMACDMABUSMOD) register, offset 0xC00, so that the
    DMA operates with extended descriptor size. When this control bit is reset to the default
    (0), the TDES4-TDES7 descriptor space is not valid and only Alternate Descriptors are
    available, with a default size of 16 bytes (4 words).

    Secondly, the DMA copies the Rx timestamps (and presumably Tx timestamps if I can get that working) into a DMA descriptor (32 bytes of memory). It cycles round 4 DMA descriptors and I would appreciate any help possible in working out how to determine which descriptor was used for the message I have just sent or received. Perhaps a device driver expert could shed some light on this.

    7. If IEEE 1588 timestamping is enabled, the DMA writes the timestamp to the current descriptor's
    RDES6 and RDES7. It then takes the receive frame's status and writes the status word to the
    current descriptor's RDES0, with the OWN bit cleared and the Last Segment (LS) bit set. If the
    host stopped the RX DMA by clearing the SR bit of the EMACDMAOPMODE register, DMA
    goes to the STOP state, otherwise the RX DMA proceeds to Step 8.

    If software has enabled timestamping through the Ethernet MAC Timestamp Control
    (EMACTIMSTCTRL) register, offset 0x700, when a valid timestamp is not available for the frame
    (for example, because the receive FIFO was full before the timestamp could be written to it), the
    DMA writes all-ones to RDES6 and RDES7. Otherwise if timestamping is not enabled, RDES6 and
    RDES7 remain unchanged.

  • Hi Charles,

    Thanks, that was the problem.
    I found a useful driver function EMACMSP432E4_processPendingTx. Initial hacks here:-

    It looks like I can add a bit in there to set the tx timestamp enable flag if the packet looks like a PTP message.

    I can also collect the timestamp and leave it somewhere useful for the application to use. My loop counter shows that it takes about 140 loops to wait for the DMA to finish and clear the DES0_TX_CTRL_OWN flag. That doesn't sound too bad, once per second or so for the PTP DELAY_REQ messages that I need t send.

    uint32_t EMAC_TxTimestampWait;
    uint32_t EMAC_TxTimestampHigh;
    uint32_t EMAC_TxTimestampLow;

    /*
    * ======== EMACMSP432E4_processPendingTx ========
    */
    static void EMACMSP432E4_processPendingTx()
    {
        uint8_t *pBuffer;
        uint32_t len;
        PBM_Handle hPkt;
        tDescriptor *pDesc;

        /*
        * If there are pending packets, send one.
        * Otherwise quit the loop.
        */
        hPkt = PBMQ_deq(&EMACMSP432E4_private.PBMQ_tx);
        if (hPkt != NULL) {

            pDesc = &(EMACMSP432E4_private.pTxDescList->pDescriptors[EMACMSP432E4_private.pTxDescList->ulWrite]);
            if (pDesc->hPkt) {
                PBM_free(hPkt);
                EMACMSP432E4_private.txDropped++;
                return;
            }

        /* Get the pointer to the buffer and the length */
        pBuffer = PBM_getDataBuffer(hPkt) + PBM_getDataOffset(hPkt);
        len = PBM_getValidLen(hPkt);

        /* Fill in the buffer pointer and length */
        pDesc->Desc.ui32Count = len;
        pDesc->Desc.pvBuffer1 = pBuffer;
        pDesc->Desc.ui32CtrlStatus = DES0_TX_CTRL_FIRST_SEG;

        bool isPTPmsg = false;
        // Test dest port in outgoing message == 319, as check for PTP
        if( (pBuffer[36] == 0x01) && (pBuffer[37] == 0x3F) )
        {
           isPTPmsg = true;
            pDesc->Desc.ui32CtrlStatus |= DES0_TX_CTRL_ENABLE_TS;
        }

        pDesc->Desc.ui32CtrlStatus |=
        (DES0_TX_CTRL_IP_ALL_CKHSUMS | DES0_TX_CTRL_CHAINED);
        pDesc->Desc.ui32CtrlStatus |= (DES0_TX_CTRL_LAST_SEG |
        DES0_TX_CTRL_INTERRUPT);
        EMACMSP432E4_private.pTxDescList->ulWrite++;
        if (EMACMSP432E4_private.pTxDescList->ulWrite == NUM_TX_DESCRIPTORS) {
            EMACMSP432E4_private.pTxDescList->ulWrite = 0;
        }
        pDesc->hPkt = hPkt;
        pDesc->Desc.ui32CtrlStatus |= DES0_TX_CTRL_OWN;

        EMACMSP432E4_private.txSent++;

        EMACTxDMAPollDemand(EMAC0_BASE);

        if(isPTPmsg)
        {
            uint32_t waitCount = 0;
            while(pDesc->Desc.ui32CtrlStatus & DES0_TX_CTRL_OWN)
            {
                waitCount++;
            }
            EMAC_TxTimestampWait = waitCount;
            EMAC_TxTimestampHigh = pDesc->Desc.ui32IEEE1588TimeHi;
            EMAC_TxTimestampLow = pDesc->Desc.ui32IEEE1588TimeLo;
        }
    }

    return;
    }

  • Hi Jim,

     Glad that your problem is solved. I will also bookmark this post for future reference as your code snippet will definitely benefit others. Thanks. 

  • FYI,

    A similar hack in EMACMSP432E4_handleRx grabs the receive timestamp for PTP packets.

    uint32_t EMAC_RxTimestampHigh;
    uint32_t EMAC_RxTimestampLow;

    /*
    * ======== EMACMSP432E4_handleRx ========
    */
    static void EMACMSP432E4_handleRx()
    {
    PBM_Handle hPkt;
    PBM_Handle hPktNew;
    int32_t len;
    tDescriptorList *pDescList;
    uint32_t ulDescEnd;
    int32_t descCount = -1;
    uint32_t ui32Config;
    uint32_t ui32Mode;
    uint32_t ui32FrameSz;
    uint32_t ui32CtrlStatus;

    /* Get a pointer to the receive descriptor list. */
    pDescList = EMACMSP432E4_private.pRxDescList;

    /* Determine where we start and end our walk of the descriptor list */
    ulDescEnd = pDescList->ulRead ?
    (pDescList->ulRead - 1) : (pDescList->ulNumDescs - 1);

    /* Step through the descriptors that are marked for CPU attention. */
    while (pDescList->ulRead != ulDescEnd) {
    descCount++;

    /* Does the current descriptor have a buffer attached to it? */
    hPkt = pDescList->pDescriptors[pDescList->ulRead].hPkt;
    if (hPkt) {
    ui32CtrlStatus = pDescList->pDescriptors[pDescList->ulRead].Desc.ui32CtrlStatus;

    /* Determine if the host has filled it yet. */
    if (ui32CtrlStatus & DES0_RX_CTRL_OWN) {
    /* The DMA engine still owns the descriptor so we are finished. */
    break;
    }

    /* Yes - does the frame contain errors? */
    if (ui32CtrlStatus & DES0_RX_STAT_ERR) {
    /*
    * This is a bad frame. Update the relevant statistics and
    * then discard it.
    */

    /*
    * Check the EMAC configuration to see if RX h/w checksums are
    * enabled. (The last 2 parameters are don't cares here)
    */
    ui32Config = 0;
    EMACConfigGet(EMAC0_BASE, &ui32Config, &ui32Mode, &ui32FrameSz);
    if (ui32Config & EMAC_CONFIG_CHECKSUM_OFFLOAD) {
    /* RX h/w checksums are enabled, look for checksum errors */

    /* First check if the Frame Type bit is set */
    if (ui32CtrlStatus & DES0_RX_STAT_FRAME_TYPE) {
    /* Now, if bit 7 is reset and bit 0 is set: */
    if (!(ui32CtrlStatus & EMAC_DES0_RX_STAT_IPHDR_CHKSM_ERR) &&
    (ui32CtrlStatus & EMAC_DES0_RX_STAT_PAYLOAD_CHKSM_ERR)) {
    /* Checksum error detected in the IP payload */
    EMACMSP432E4_private.rxPayloadChksmErrors++;
    }
    /* Else if bit 7 and bit 0 are both set */
    else if ((ui32CtrlStatus & EMAC_DES0_RX_STAT_IPHDR_CHKSM_ERR) &&
    (ui32CtrlStatus & EMAC_DES0_RX_STAT_PAYLOAD_CHKSM_ERR)) {
    /* Checksum error in both IP header & payload */
    EMACMSP432E4_private.rxIpHdrChksmErrors++;
    EMACMSP432E4_private.rxPayloadChksmErrors++;
    }

    }
    }

    EMACMSP432E4_private.rxDropped++;
    EMACMSP432E4_primeRx(hPkt,
    &(pDescList->pDescriptors[pDescList->ulRead]));
    pDescList->ulRead++;
    break;
    }
    else {
    /* Allocate a new buffer for this descriptor */
    hPktNew = PBM_alloc(RX_BUFFER_SIZE);
    if (hPktNew == NULL) {
    /*
    * Leave the packet in the descriptor and owned by the
    * driver. Process when the next interrupt occurs.
    */
    EMACMSP432E4_private.pbmAllocErrors++;
    break;
    }

    /* This is a good frame so pass it up the stack. */
    len = (pDescList->pDescriptors[pDescList->ulRead].Desc.ui32CtrlStatus &
    DES0_RX_STAT_FRAME_LENGTH_M) >> DES0_RX_STAT_FRAME_LENGTH_S;

    char * pBuffer = pDescList->pDescriptors[pDescList->ulRead].Desc.pvBuffer1;

    // Test src port in incoming message == 319, as check for PTP
    if( (pBuffer[34] == 0x01) && (pBuffer[35] == 0x3F) )
    {
        EMAC_RxTimestampHigh = pDescList->pDescriptors[pDescList->ulRead].Desc.ui32IEEE1588TimeHi;
        EMAC_RxTimestampLow = pDescList->pDescriptors[pDescList->ulRead].Desc.ui32IEEE1588TimeLo;
    }

    /* Remove the CRC */
    PBM_setValidLen(hPkt, len - CRC_SIZE_BYTES);

    /*
    * Place the packet onto the receive queue to be handled in the
    * EMACMSP432E4_pkt_service function (which is called by the
    * NDK stack).
    */
    PBMQ_enq(&EMACMSP432E4_private.PBMQ_rx, hPkt);

    /* Update internal statistic */
    EMACMSP432E4_private.rxCount++;

    /*
    * Notify NDK stack of pending Rx Ethernet packet and
    * that it was triggered by an external event.
    */
    STKEVENT_signal(EMACMSP432E4_private.hEvent, STKEVENT_ETHERNET,
    1);

    /* Prime the receive descriptor back up for future packets */
    EMACMSP432E4_primeRx(hPktNew,
    &(pDescList->pDescriptors[pDescList->ulRead]));
    }
    }

    /* Move on to the next descriptor in the chain, taking care to wrap. */
    pDescList->ulRead++;
    if (pDescList->ulRead == pDescList->ulNumDescs) {
    pDescList->ulRead = 0;
    }
    }

    /*
    * Update the desciptorLoopCount. This shows how frequently we are cycling
    * through x DMA descriptors, where x is the index of this array. So if
    * descriptorLoopcount[1] = 32, we had to cycle through 2 descriptors here
    * 32 times.
    */
    if(descCount >= 0 && descCount < NUM_RX_DESCRIPTORS) {
    EMACMSP432E4_private.descriptorLoopCount[descCount]++;
    }
    }