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.

[FAQ]: AM26x: How to enable CPTS packet timestamping in CPSW

Part Number: AM2612


Hi experts,

Can you please provide the steps to enable Packet time-stamping in CPSW. What changes do I need in my application code and syscfg to enable the hardware time-stamping. Can you provide sample code snippets for the same.

  • CPSW supports time-stamping packets in hardware via the CPTS sub-module. To read more about CPSW and CPTS, please refer: https://dev.ti.com/tirex/explore/node?node=A__AEIJm0rwIeU.2P1OBWwlaA__AM26X-ACADEMY__t0CaxbG__LATEST and the Device TRM's section for CPSW. 

    When packet transmission or reception occurs on a port in CPSW, the CPTS interface checks for valid TimeSync packet and the CPSW decoder pushes Ethernet Transmit/Receive event to the event FIFO. The time-stamping support is available only for valid TimeSync packets, even though the time-stamps are stored for all packets received, the transmit/receive event is pushed only for valid TimeSync packets.

    The application can obtain the timestamp values (logged by the HW) by using the ENET IOCTLs which are already supported through the Enet-LLD driver (ENET_TIMESYNC_IOCTL_GET_ETH_RX_TIMESTAMP and ENET_TIMESYNC_IOCTL_GET_ETH_TX_TIMESTAMP).

    For AM26x devices, please refer the steps and the code snippets (Relevant to the MCU_PLUS_SDK) below:

    HOW DOES PACKET TIME-STAMPING WORK?

    Packet receive event:

    Ethernet port generates time synchronization events for valid received time sync packets. For every packet received on the Ethernet port, a timestamp will be captured by the receive module inside the CPTS for the corresponding port. The CPSW decoder determines if the packet is a valid ethernet receive time synchronization event. once validates, the Rx interface will push an Ethernet receive event into the event FIFO. The SW can then read the timestamp based on this event.

    Packet transmit event:

    Once the CPSW determines the transmitted packet is valid time synchronization packet, then the time stamp for the transmit event will not be generated until the start of frame delimiter of the packet is actually transmitted. The start of frame delimiter will be sampled on every rising and falling edge of the CPTS_RFT_CLK. Once the packet is transmitted, then the TX interface will push an Ethernet transmit event into the event FIFO.

    ENABLING PACKET TIME-STAMPING IN YOUR APPLICATION:

    Syscfg changes:

    1. Open the example.syscfg for the application
    2. Navigate to Enet (CPSW), then to MAC Port config
    3. Make sure the MAC Port in use is enabled and MAC loopback mode is not selected as packet time-stamping is not supported in MAC loopback mode. 
    4. In CPTS configuration, set Timestamp assertion priority as "ASSERT_HIGH".

    Application changes:

    To enable CPTS packet time-stamping, we create some util functions to call ENET IOCTLs and fetch timestamp values. We also need to populate the CPTS params and configure the CPTS module before application (packet transmission/reception) starts. The below code snippets can be re-used in the application.

    1. Function to fill CPTS params and Enable CPTS Timestamp event. This function should be called in the application before checking MAC Port-link up status, usually done as a part of EnetApp_open() function.
      if (status == ENET_SOK)
      {
          EnetApp_enableCptsTsEvents(perCtxts);
      }

      void EnetApp_fillTsEventPrms(CpswMacPort_TsEventCfg *tsPortEventCfg)
      {
          tsPortEventCfg->commonPortIpCfg.ttlNonzeroEn = true;
          tsPortEventCfg->commonPortIpCfg.tsIp107En = false;
          tsPortEventCfg->commonPortIpCfg.tsIp129En = false;
          tsPortEventCfg->commonPortIpCfg.tsIp130En = false;
          tsPortEventCfg->commonPortIpCfg.tsIp131En = false;
          tsPortEventCfg->commonPortIpCfg.tsIp132En = false;
          tsPortEventCfg->commonPortIpCfg.tsPort319En = true;
          tsPortEventCfg->commonPortIpCfg.tsPort320En = true;
          tsPortEventCfg->commonPortIpCfg.unicastEn = false;
          tsPortEventCfg->domainOffset = 4U;
          tsPortEventCfg->ltype2En = false;
          tsPortEventCfg->rxAnnexDEn = true;
          tsPortEventCfg->rxAnnexEEn = true;
          tsPortEventCfg->rxAnnexFEn = true;
          tsPortEventCfg->txAnnexDEn = true;
          tsPortEventCfg->txAnnexEEn = true;
          tsPortEventCfg->txAnnexFEn = true;
          tsPortEventCfg->txHostTsEn = true;
          tsPortEventCfg->mcastType = 0U;
          tsPortEventCfg->messageType = 0xFFFFU;
          tsPortEventCfg->seqIdOffset = 30U;
          /* VLAN untagged */
          tsPortEventCfg->rxVlanType = ENET_MACPORT_VLAN_TYPE_SINGLE_TAG;
          tsPortEventCfg->txVlanType = ENET_MACPORT_VLAN_TYPE_SINGLE_TAG;
          tsPortEventCfg->vlanLType1 = 0U;
          tsPortEventCfg->vlanLType2 = 0U;
      }
      
      int32_t EnetApp_enableCptsTsEvents(EnetApp_PerCtxt* perCtxt)
      {
          int32_t status;
          CpswMacPort_EnableTsEventInArgs enableTsEventInArgs;
          CpswCpts_RegisterStackInArgs enableTxEvtInArgs;
          Enet_IoctlPrms prms;
      
          EnetApp_fillTsEventPrms(&enableTsEventInArgs.tsEventCfg);
      
          enableTsEventInArgs.macPort = perCtxt->macPort[ENET_MAC_PORT_1];
          ENET_IOCTL_SET_IN_ARGS(&prms, &enableTsEventInArgs);
          ENET_IOCTL(perCtxt->hEnet,
                     gEnetApp.coreId,
                     CPSW_MACPORT_IOCTL_ENABLE_CPTS_EVENT,
                     &prms,
                     status);
          if (status == ENET_SOK)
          {
              enableTxEvtInArgs.eventNotifyCb = &EnetApp_cptsEvtNotifyFxn;
              enableTxEvtInArgs.eventNotifyCbArg = perCtxt;
              ENET_IOCTL_SET_IN_ARGS(&prms, &enableTxEvtInArgs);
      
              ENET_IOCTL(perCtxt->hEnet,
                          gEnetApp.coreId,
                         CPSW_CPTS_IOCTL_REGISTER_STACK,
                         &prms,
                         status);
          }
          return status;
      }


    2. After the application transmits a packet or receives a packet, the application can use Semaphores and pend them in the application, wait for an ISR for CPTS event interrupt. This ISR will be used to inform the application to read the timestamp after successful TimeSync packet transmission/reception.
      #define ENETAPP_CPTS_EVENTS_COUNTING_SEM_MAX_VAL (16U)
      
      SemaphoreP_Object cptsEvtSemObj;
      
      status = SemaphoreP_constructCounting(&cptsEvtSemObj, 0, ENETAPP_CPTS_EVENTS_COUNTING_SEM_MAX_VAL);
      DebugP_assert(SystemP_SUCCESS == status);
      
      void EnetApp_cptsEvtNotifyFxn(void *pAppData, CpswCpts_Event *pEventInfo)
      {
          SemaphoreP_post(&cptsEvtSemObj);
      }


    3. Functions to get Tx and Rx timestamps from event FIFO. This can be called in the application when packet reception or transmission is completed (completion indicated by semaphore being posted in ISR).
      uint32_t EnetApp_getTxTimestamp(EnetApp_PerCtxt *perCtxt,
                                      EnetTimeSync_MsgType txFrameType,
                                      uint8_t txPort,
                                      uint16_t seqId)
      {
          int32_t status = ENET_SOK;
          Enet_IoctlPrms prms;
          EnetTimeSync_GetEthTimestampInArgs inArgs;
          uint64_t tsVal = 0U;
      
          if (perCtxt != NULL)
          {
              inArgs.msgType = txFrameType;
              inArgs.seqId = seqId;
              inArgs.portNum = txPort;
              inArgs.domain  = 0U;
      
              if (status == ENET_SOK)
              {
                  ENET_IOCTL_SET_INOUT_ARGS(&prms, &inArgs, &tsVal);
                  ENET_IOCTL(perCtxt->hEnet,
                                      gEnetApp.coreId,
                                      ENET_TIMESYNC_IOCTL_GET_ETH_TX_TIMESTAMP,
                                      &prms,
                                      status);
      
              }
          }
          else
          {
              status = ENET_EBADARGS;
          }
      
          return (tsVal & 0xFFFFFFFF);
      }
      
      uint32_t EnetApp_getRxTimestamp(EnetApp_PerCtxt *perCtxt,
                                      EnetTimeSync_MsgType rxFrameType,
                                      uint8_t rxPort,
                                      uint16_t seqId)
      {
          int32_t status = ENET_SOK;
          Enet_IoctlPrms prms;
          EnetTimeSync_GetEthTimestampInArgs inArgs;
          uint64_t tsVal = 0U;
      
          if (perCtxt != NULL)
          {
              inArgs.msgType = rxFrameType;
              inArgs.seqId   = seqId;
              inArgs.portNum = rxPort;
              inArgs.domain  = 0U;
      
              if (status == ENET_SOK)
              {
                  ENET_IOCTL_SET_INOUT_ARGS(&prms, &inArgs, &tsVal);
                  ENET_IOCTL(perCtxt->hEnet,
                              gEnetApp.coreId,
                             ENET_TIMESYNC_IOCTL_GET_ETH_RX_TIMESTAMP,
                             &prms,
                             status);
      
              }
          }
          else
          {
              status = ENET_EBADARGS;
          }
      
          return (tsVal & 0xFFFFFFFF);
      }
      
      uint32_t EnetApp_getCurrentTime(EnetApp_PerCtxt *perCtxt)
      {
          int32_t status = ENET_SOK;
          Enet_IoctlPrms prms;
          uint64_t tsVal = 0U;
      
          if (perCtxt != NULL)
          {
              /* Software Time stamp Push event */
              ENET_IOCTL_SET_OUT_ARGS(&prms, &tsVal);
              ENET_IOCTL(perCtxt->hEnet,
                          gEnetApp.coreId,
                         ENET_TIMESYNC_IOCTL_GET_CURRENT_TIMESTAMP,
                         &prms,
                         status);
              EnetAppUtils_assert(status == ENET_SOK);
      
          }
          return (tsVal&0xFFFFFFFF);
      }


    4. Fetching timestamps in application
      /* Wait for Rx Timestamp event of the packet */
      SemaphoreP_pend(&cptsEvtSemObj, SystemP_WAIT_FOREVER);
      
      /* Get the Rx Timestamp of the packet */
      uint32_t pktRxTime = EnetApp_getRxTimestamp(gEnetApp.perCtxt, ENET_TIMESYNC_MESSAGE_PDELAY_REQ, 0, 0);
      EnetAppUtils_print("Rx Timestamp: %6u ns\r\n", (pktRxTime));
      
      /* Wait for Tx Timestamp evet of the packet (that is sent in (1) */
      SemaphoreP_pend(&cptsEvtSemObj, SystemP_WAIT_FOREVER);
      
      /* Get the Tx Timestamp of the packet */
      uint32_t pktTxTime = EnetApp_getTxTimestamp(gEnetApp.perCtxt, ENET_TIMESYNC_MESSAGE_PDELAY_REQ, 0, 0);
      EnetAppUtils_print("Tx Timestamp: %6u ns\r\n", ( pktTxTime ));
      
      

    A sample TimeSync packet that can be time-stamped by CPSW (can be sent by the host application or externally):

    uint8_t gPtpMsg[] =
    {
            0xD4, 0x81, 0xD7, 0xE7, 0xC9, 0x3B, /* Destination MAC addr */
            0x70, 0xFF, 0x76, 0x1F, 0xCF, 0xB0, /* Source MAC addr */
            0x08, 0x06, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x02, 0x08, 0x01, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0xf4, 0x84,  
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x00, 0x00,
    };

    Next, Build and run your application. On transmission/reception of a valid TimeSync packet, the timestamp should be printed.

    Regards,
    Shaunak

    Checkout other FAQs for AM263x/AM263Px

    Checkout other FAQs for AM261x