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.

Packet accelerator CRC calculation failure



Hi everyone,

I'am using MSDK 2.1.2.6, custom board with C6670 processor.

This post is related to my previous one. 

My gues is that I'am having problems with Packet accelerator setup. If we go back and look into pdk_C6670_1_1_2_6\packages\ti\drv\pa\example\emacExample\ cppi_qmss_mgmt.c file, we can find a CycleDelay (10000) line (#753) in the SendPacket() function. What does that delay stends for? I know that comment said "Give PA some time", but what exactly is PA doing at this time? How does PA determine the type of CRC polynom he supoosed to use if none was specified during setup?

The reason I'am asking is following: If I will remove completely this delay and DSP will keep pushing packets one by one the PA starting to generate wrong CRC. You can find screenshot 1.tif from Wireshark.  Packet numer 2 is a comand from Host application to DSP (CRC generated by network card so it's the case of CRC offload). 3rd packet is a response from DSP. Due to it's small length Everything is fine. But strating from packet number 7 when DSP transmitting large data packets the PA generating wrong CRC and this packets discarded by the Host application. The last one 22 packet has a correct generated CRC, as I see it bacause PA was not interrupted some time by the DSP and has enough time, like in the case of Delay.

After this I have increased Delay to CycleDelay (8000), but still not 10000 as it was before. The result you can find on the Screenshot 2.tif. In that case wrong CRC calculation apper randomly. It could fail on 74th big packet, or it can be on 200 packet. Everytime before pushing packet into PA queue, I'am checking Qmss_getQueueEntryCount(gvTxQ[8]) for 644 queue and number of entry count is always 0, so no packets truncated in the queue.

So as i understand the PA should be a pipeline device and if it can handle taking out packets out of queue is should handle the CRC  calculation properly. Any idea for the reason of such behavior? 

1.tif2.tif

Best regards,

Pavlo!

  • Hi Pavlo,
    I will ask our experts to comment here. Thank you for your patience.
  • Pavlo,

    I need to reproduce the case that Tx queue is not full but CRC is wrong and investigate.

    Regards, Eric
  • Pavlo,

    I am trying with MCSDK 2.1.2.6 PA-EMAC example, with loopback mode to CPSW_LOOPBACK_NONE. If I totally remove the cycleDelay() in the Tx, there were packet loss as we discussed early.

    In the routine, hard-coded pktMatch[] was sent out, with fixed IP and UDP checksum, see attached picture. How did you use the PA to calculate the checksum and fill-into the packets before sending out?

    Regards, Eric

  • Eric,

    I replace the standard pktMatch[] with UDP packet composer function. Actually this is the code which we are using in our project. I have modified the pdk_C6670_1_1_2_6\packages\ti\drv\pa\example\emacExample\ cppi_qmss_mgmt.c file and cpsw_singlecore.c with required functions, so please, replace it in your project and we will be able to synchronize our projects for better communication. When I made this modifications I also do not observe any CRC degradation, which is very strange for me, since config and sending part are almost identical. So pictures from the head of the topic was made in our custom project. But when I modify the example project in the same way, I can't replicate the CRC problem.

    Anyway without Delay packets are still remain missing on the Host side for some reason.

    Regards,

    Pavlo!

    /**  
     * @file cppi_qmss_mgmt.c
     *
     * @brief 
     *  This file holds all the APIs required to configure CPPI/QMSS LLDs and 
     *  to send/receive data using PA/QM.
     *
     *  \par
     *  ============================================================================
     *  @n   (C) Copyright 2009, Texas Instruments, Inc.
     * 
     *  Redistribution and use in source and binary forms, with or without 
     *  modification, are permitted provided that the following conditions 
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright 
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the 
     *    documentation and/or other materials provided with the   
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
    */
    #include <cpsw_singlecore.h>
    #include <ti/drv/qmss/qmss_firmware.h>
    /* PA LLD include */
    #include <ti/drv/pa/pa.h>
    #include <ti/drv/pa/pasahost.h>
    
    /* QMSS device specific configuration */
    extern Qmss_GlobalConfigParams  qmssGblCfgParams;
    /* CPPI device specific configuration */
    extern Cppi_GlobalConfigParams  cppiGblCfgParams;
    
    
    #if ( !defined( _LITTLE_ENDIAN ) && !defined( _BIG_ENDIAN ) ) \
    ||  ( defined(_LITTLE_ENDIAN ) && defined( _BIG_ENDIAN ) )
    #error either _LITTLE_ENDIAN or _BIG_ENDIAN must be defined
    #endif
    
    /* Host Descriptor Region - [Size of descriptor * Number of descriptors] 
     *
     * MUST be 16 byte aligned.
     */
    #pragma DATA_ALIGN (gHostDesc, 16)
    UInt8                                   gHostDesc[SIZE_HOST_DESC * NUM_HOST_DESC];
    
    /* High Priority QM Rx Interrupt Threshold */
    #define		RX_INT_THRESHOLD			1u
    
    /* Accumulator channel to use */
    #define		PA_ACC_CHANNEL_NUM			0u
    
    /* High Priority Accumulator List - [((Interrupt Threshold + 1) * 2)] 
     *
     * MUST be 16 byte aligned.
     *
     * The High priority accumulator list consists of 2 buffers Ping and
     * Pong each consisting of the following entries:
     *
     * (1)  Entry count -   specifies number of packets accumulated in
     *                      the list.
     * (2)  Descriptors -   an array of Rx packet descriptors accumulated
     *                      in this list.
     *
     * Hence the size of high priority accumulator list is calculated as 
     * follows:
     *
     * (1)  Get the interrupt threshold, i.e., maximum number of Rx
     *      packets to accumulate before an interrupt is generated.
     * (2)  Add an extra entry to the threshold to track 
     *      entry count of the list.
     * (3)  Double this to accomodate space for Ping/Pong lists.
     * (4)  Each accumulator entry is 4 bytes wide.
     *  
     * size =   ((interrupt threshold + 1) * 2) * 4 bytes
     *
     * Lets allocate here assuming that interrupt threshold is 1, i.e.,
     * interrupt on every Rxed packet.
     */
    #pragma DATA_ALIGN (gHiPriAccumList, 16)
    UInt32                                  gHiPriAccumList[(RX_INT_THRESHOLD + 1) * 2];
    
    /* CPPI/QMSS Handles used by the application */
    Qmss_QueueHnd                           gGlobalFreeQHnd, gPaTxQHnd [NUM_PA_TX_QUEUES], gTxFreeQHnd, gRxFreeQHnd, gRxQHnd;
    Cppi_Handle                             gCpdmaHnd;
    Cppi_ChHnd                              gCpdmaTxChanHnd [NUM_PA_TX_QUEUES], gCpdmaRxChanHnd [NUM_PA_RX_CHANNELS];  
    Cppi_FlowHnd                            gRxFlowHnd;
    Bool                                    gIsPingListUsed = 0;
    
    /* Number of Tx Free descriptors to allocate */
    #define     NUM_TX_DESC                 NUM_HOST_DESC/2
    
    /* Number of Rx Free descriptors to allocate */
    #define     NUM_RX_DESC                 NUM_HOST_DESC/2
    
    /* Constructed data packet to send. */
    #pragma DATA_ALIGN(pktMatch, 16)
    UInt8 pktMatch[1518];
    //UInt8 pktMatch[] = {
    //							0x10, 0x11, 0x12, 0x13, 0x14, 0x15,                      /* Dest MAC */
    //                            0x00, 0x01, 0x02, 0x03, 0x04, 0x05,                      /* Src MAC  */
    //                            0x08, 0x00,                                              /* Ethertype = IPv4 */
    //                            0x45, 0x00, 0x00, 0x6c,                                  /* IP version, services, total length */
    //                            0x00, 0x00, 0x00, 0x00,                                  /* IP ID, flags, fragment offset */
    //                            0x05, 0x11, 0x32, 0x26,                                  /* IP ttl, protocol (UDP), header checksum */
    //                            0xc0, 0xa8, 0x01, 0x01,                                  /* Source IP address */
    //                            0xc0, 0xa8, 0x01, 0x0a,                                  /* Destination IP address */
    //                            0x12, 0x34, 0x56, 0x78,                                  /* UDP source port, dest port */
    //                            0x00, 0x58, 0x1d, 0x18,                                  /* UDP len, UDP checksum */
    //                            0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,          /* 80 bytes of payload data */
    //                            0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
    //                            0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
    //                            0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
    //                            0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
    //                            0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
    //                            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
    //                            0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
    //                            0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
    //                            0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81 };
    
    /* Tx/Rx packet counters */
    volatile UInt32						gTxCounter = 0, gRxCounter = 0;
    
    /* High Priority Accumulation Interrupt Service Handler for this application */
    Void Cpsw_RxISR (Void);
    
    
    /** ============================================================================
     *   @n@b Convert_CoreLocal2GlobalAddr
     *
     *   @b Description
     *   @n This API converts a core local L2 address to a global L2 address.
     *
     *   @param[in]  
     *   @n addr            L2 address to be converted to global.
     * 
     *   @return    UInt32
     *   @n >0              Global L2 address
     * =============================================================================
     */
    UInt32 Convert_CoreLocal2GlobalAddr (UInt32  addr)
    {
    	UInt32 coreNum;
    
        /* Get the core number. */
        coreNum = CSL_chipReadReg(CSL_CHIP_DNUM); 
    
        /* Compute the global address. */
        return ((1 << 28) | (coreNum << 24) | (addr & 0x00ffffff));
    }    
    
    /** ============================================================================
     *   @n@b Init_Qmss
     *
     *   @b Description
     *   @n This API initializes the QMSS LLD.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return    Int32
     *              -1      -   Error
     *              0       -   Success
     * =============================================================================
     */
    Int32 Init_Qmss (Void)
    {
        Int32                       result;
        Qmss_MemRegInfo             memCfg;
        Qmss_InitCfg                qmssInitConfig;
        Cppi_DescCfg                cppiDescCfg;
        UInt32                      numAllocated;
    
        /* Initialize QMSS */
        memset (&qmssInitConfig, 0, sizeof (Qmss_InitCfg));
    
        /* Set up QMSS configuration */
    
        /* Use internal linking RAM */
        qmssInitConfig.linkingRAM0Base  =   0;   
        qmssInitConfig.linkingRAM0Size  =   0;
        qmssInitConfig.linkingRAM1Base  =   0x0;
        qmssInitConfig.maxDescNum       =   NUM_HOST_DESC;
        
        qmssInitConfig.pdspFirmware[0].pdspId = Qmss_PdspId_PDSP1;
    #ifdef _LITTLE_ENDIAN    
        qmssInitConfig.pdspFirmware[0].firmware = (void *) &acc48_le;
        qmssInitConfig.pdspFirmware[0].size = sizeof (acc48_le);
    #else
        qmssInitConfig.pdspFirmware[0].firmware = (void *) &acc48_be;
        qmssInitConfig.pdspFirmware[0].size = sizeof (acc48_be);
    #endif    
    
        /* Initialize the Queue Manager */
        result = Qmss_init (&qmssInitConfig, &qmssGblCfgParams);
        if (result != QMSS_SOK)
        {
            System_printf ("Error initializing Queue Manager SubSystem, Error code : %d\n", result);
            return -1;
        }
    
        /* Start Queue manager on this core */
        Qmss_start ();
    
        /* Setup the descriptor memory regions. 
         *
         * The Descriptor base addresses MUST be global addresses and
         * all memory regions MUST be setup in ascending order of the
         * descriptor base addresses.
         */
    
        /* Initialize and setup CPSW Host Descriptors required for example */
        memset (gHostDesc, 0, SIZE_HOST_DESC * NUM_HOST_DESC);
        memCfg.descBase             =   (UInt32 *) Convert_CoreLocal2GlobalAddr ((UInt32) gHostDesc);
        memCfg.descSize             =   SIZE_HOST_DESC;
        memCfg.descNum              =   NUM_HOST_DESC;
        memCfg.manageDescFlag       =   Qmss_ManageDesc_MANAGE_DESCRIPTOR;
        memCfg.memRegion            =   Qmss_MemRegion_MEMORY_REGION0;
        memCfg.startIndex           =   0;
    
        /* Insert Host Descriptor memory region */
        result = Qmss_insertMemoryRegion(&memCfg);
        if (result == QMSS_MEMREGION_ALREADY_INITIALIZED)
        {
            System_printf ("Memory Region %d already Initialized \n", memCfg.memRegion);
        }
        else if (result < QMSS_SOK)
        {
            System_printf ("Error: Inserting memory region %d, Error code : %d\n", memCfg.memRegion, result);
            return -1;
        }    
    
        /* Initialize all the descriptors we just allocated on the
         * memory region above. Setup the descriptors with some well
         * known values before we use them for data transfers.
         */
        memset (&cppiDescCfg, 0, sizeof (cppiDescCfg));
        cppiDescCfg.memRegion       =   Qmss_MemRegion_MEMORY_REGION0;
        cppiDescCfg.descNum         =   NUM_HOST_DESC;
        cppiDescCfg.destQueueNum    =   QMSS_PARAM_NOT_SPECIFIED;     
        cppiDescCfg.queueType       =   Qmss_QueueType_GENERAL_PURPOSE_QUEUE;
        cppiDescCfg.initDesc        =   Cppi_InitDesc_INIT_DESCRIPTOR;
        cppiDescCfg.descType        =   Cppi_DescType_HOST;
        
        /* By default:
         *      (1) Return descriptors to tail of queue 
         *      (2) Always return entire packet to this free queue
         *      (3) Set that PS Data is always present in start of SOP buffer
         *      (4) Configure free q num < 4K, hence qMgr = 0
         *      (5) Recycle back to the same Free queue by default.
         */
        cppiDescCfg.returnPushPolicy            =   Qmss_Location_TAIL;    
        cppiDescCfg.cfg.host.returnPolicy       =   Cppi_ReturnPolicy_RETURN_ENTIRE_PACKET;    
        cppiDescCfg.cfg.host.psLocation         =   Cppi_PSLoc_PS_IN_DESC;         
        cppiDescCfg.returnQueue.qMgr            =   0;    
        cppiDescCfg.returnQueue.qNum            =   QMSS_PARAM_NOT_SPECIFIED; 
        cppiDescCfg.epibPresent                 =   Cppi_EPIB_EPIB_PRESENT;
        
        /* Initialize the descriptors, create a free queue and push descriptors to a global free queue */
        if ((gGlobalFreeQHnd = Cppi_initDescriptor (&cppiDescCfg, &numAllocated)) <= 0)
        {
            System_printf ("Error Initializing Free Descriptors, Error: %d \n", gGlobalFreeQHnd);
            return -1;
        }        
       
        /* Queue Manager Initialization Done */
        return 0;
    }
    
    /** ============================================================================
     *   @n@b Init_Cppi
     *
     *   @b Description
     *   @n This API initializes the CPPI LLD, opens the PASS CPDMA and opens up
     *      the Tx, Rx channels required for data transfers.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return    Int32
     *              -1      -   Error
     *              0       -   Success
     * =============================================================================
     */
    Int32 Init_Cppi (Void)
    {
        Int32                       result, i;        
        Cppi_CpDmaInitCfg           cpdmaCfg;
        UInt8                       isAllocated;        
        Cppi_TxChInitCfg            txChCfg;
        Cppi_RxChInitCfg            rxChInitCfg;
    
        /* Initialize CPPI LLD */
        result = Cppi_init (&cppiGblCfgParams);
        if (result != CPPI_SOK)
        {
            System_printf ("Error initializing CPPI LLD, Error code : %d\n", result);
            return -1;
        }
    
        /* Initialize PASS CPDMA */
        memset (&cpdmaCfg, 0, sizeof (Cppi_CpDmaInitCfg));
        cpdmaCfg.dmaNum     = Cppi_CpDma_PASS_CPDMA;
        if ((gCpdmaHnd = Cppi_open (&cpdmaCfg)) == NULL)
        {
            System_printf ("Error initializing CPPI for PASS CPDMA %d \n", cpdmaCfg.dmaNum);
            return -1;
        }    
    
        /* Open all CPPI Tx Channels. These will be used to send data to PASS/CPSW */             
        for (i = 0; i < NUM_PA_TX_QUEUES; i ++)
        {
            txChCfg.channelNum      =   i;       /* CPPI channels are mapped one-one to the PA Tx queues */
            txChCfg.txEnable        =   Cppi_ChState_CHANNEL_DISABLE;  /* Disable the channel for now. */
            txChCfg.filterEPIB      =   0;
            txChCfg.filterPS        =   0;
            txChCfg.aifMonoMode     =   0;
            txChCfg.priority        =   2;
            if ((gCpdmaTxChanHnd[i] = Cppi_txChannelOpen (gCpdmaHnd, &txChCfg, &isAllocated)) == NULL)
            {
                System_printf ("Error opening Tx channel %d\n", txChCfg.channelNum);
                return -1;
            }
    
            Cppi_channelEnable (gCpdmaTxChanHnd[i]);
        }
    
        /* Open all CPPI Rx channels. These will be used by PA to stream data out. */
        for (i = 0; i < NUM_PA_RX_CHANNELS; i++)
        {
            /* Open a CPPI Rx channel that will be used by PA to stream data out. */
            rxChInitCfg.channelNum  =   i; 
            rxChInitCfg.rxEnable    =   Cppi_ChState_CHANNEL_DISABLE; 
            if ((gCpdmaRxChanHnd[i] = Cppi_rxChannelOpen (gCpdmaHnd, &rxChInitCfg, &isAllocated)) == NULL)
            {
                System_printf ("Error opening Rx channel: %d \n", rxChInitCfg.channelNum);
                return -1;
            }
    
            /* Also enable Rx Channel */
            Cppi_channelEnable (gCpdmaRxChanHnd[i]);    
        }
        
        /* Clear CPPI Loobpack bit in PASS CDMA Global Emulation Control Register */
        Cppi_setCpdmaLoopback(gCpdmaHnd, 0);   
    
        /* CPPI Init Done. Return success */
        return 0;
    }    
    
    /** ============================================================================
     *   @n@b Setup_Tx
     *
     *   @b Description
     *   @n This API sets up all relevant data structures and configuration required
     *      for sending data to PASS/Ethernet. It sets up a Tx free descriptor queue,
     *      PASS Tx queues required for send.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return    Int32
     *              -1      -   Error
     *              0       -   Success
     * =============================================================================
     */
    Int32 Setup_Tx (Void)
    {
        UInt8                       isAllocated, i;        
        Qmss_Queue                  qInfo;
        Ptr                   		pCppiDesc;
    
        /* Open all Transmit (Tx) queues. 
         *
         * These queues are used to send data to PA PDSP/CPSW.
         */
        for (i = 0; i < NUM_PA_TX_QUEUES; i ++)
        {
                
            if ((gPaTxQHnd[i] = Qmss_queueOpen (Qmss_QueueType_PASS_QUEUE, QMSS_PARAM_NOT_SPECIFIED, &isAllocated)) < 0)
            {
                System_printf ("Error opening PA Tx queue \n");
                return -1;
            }            
        }
    
        /* Open a Tx Free Descriptor Queue (Tx FDQ). 
         *
         * This queue will be used to hold Tx free decriptors that can be filled
         * later with data buffers for transmission onto wire.
         */
        if ((gTxFreeQHnd = Qmss_queueOpen (Qmss_QueueType_STARVATION_COUNTER_QUEUE, QMSS_PARAM_NOT_SPECIFIED, &isAllocated)) < 0)
        {
            System_printf ("Error opening Tx Free descriptor queue \n");
            return -1;
        }            
    
        qInfo = Qmss_getQueueNumber (gTxFreeQHnd);
    
        /* Attach some free descriptors to the Tx free queue we just opened. */
        for (i = 0; i < NUM_TX_DESC; i++)
        {
            /* Get a free descriptor from the global free queue we setup 
             * during initialization.
             */
            if ((pCppiDesc = Qmss_queuePop (gGlobalFreeQHnd)) == NULL)
            {
                break;                
            }
    
            /* The descriptor address returned from the hardware has the 
             * descriptor size appended to the address in the last 4 bits.
             *
             * To get the true descriptor size, always mask off the last 
             * 4 bits of the address.
             */
            pCppiDesc = (Ptr) ((UInt32) pCppiDesc & 0xFFFFFFF0);
    
            /* Setup the Completion queue:
             *
             * Setup the return policy for this desc to return to the free q we just
             * setup instead of the global free queue.
             */
            Cppi_setReturnQueue ((Cppi_DescType) Cppi_DescType_HOST, pCppiDesc, qInfo);
    
            /* Push descriptor to Tx free queue */
            Qmss_queuePushDescSize (gTxFreeQHnd, pCppiDesc, SIZE_HOST_DESC);           
        }
        if (i != NUM_TX_DESC)
        {
            System_printf ("Error allocating Tx free descriptors \n");            
            return -1;
        }
    
        /* All done with Rx configuration. Return success. */
        return 0;
    }
    
    /** ============================================================================
     *   @n@b Setup_Rx
     *
     *   @b Description
     *   @n This API sets up all relevant data structures and configuration required
     *      for receiving data from PASS/Ethernet. It sets up a Rx free descriptor queue
     *      with some empty pre-allocated buffers to receive data, and an Rx queue
     *      to which the Rxed data is streamed for the example application. This API
     *      also sets up the QM high priority accumulation interrupts required to
     *      receive data from the Rx queue.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return    Int32
     *              -1      -   Error
     *              0       -   Success
     * =============================================================================
     */
    Int32 Setup_Rx (Void)
    {
        Int32                       result, vectId;
        UInt8                       isAllocated, accChannelNum, i;        
        UInt16                      numAccEntries, intThreshold;
        Qmss_Queue                  rxFreeQInfo, rxQInfo;
        Ptr                   		pCppiDesc;
        Qmss_AccCmdCfg              accCfg;
        Cppi_RxFlowCfg              rxFlowCfg;
        Int16                       eventId;
        Ptr                         pDataBuffer;
        Uint32                      mySWInfo[] = {0x11112222, 0x33334444};
        
        /* Open a Receive (Rx) queue. 
         *
         * This queue will be used to hold all the packets received by PASS/CPSW
         *
         * Open the next available High Priority Accumulation queue for Rx.
         */
        if ((gRxQHnd = Qmss_queueOpen (Qmss_QueueType_HIGH_PRIORITY_QUEUE, QMSS_PARAM_NOT_SPECIFIED, &isAllocated)) < 0)
        {
            System_printf ("Error opening a High Priority Accumulation Rx queue \n");
            return -1;
        }            
        rxQInfo = Qmss_getQueueNumber (gRxQHnd);
    
        /* Setup high priority accumulation interrupts on the Rx queue. 
         *
         * Let's configure the accumulator with the following settings:
         *      (1) Interrupt pacing disabled.
         *      (2) Interrupt on every received packet
         */
        intThreshold    =   RX_INT_THRESHOLD;
        numAccEntries   =   (intThreshold + 1) * 2;
        accChannelNum   =   PA_ACC_CHANNEL_NUM;
    
        /* Initialize the accumulator list memory */
        memset ((Void *) gHiPriAccumList, 0, numAccEntries * 4);
    
        /* Ensure that the accumulator channel we are programming is not 
         * in use currently.
         */
        result = Qmss_disableAccumulator (Qmss_PdspId_PDSP1, accChannelNum);
        if (result != QMSS_ACC_SOK && result != QMSS_ACC_CHANNEL_NOT_ACTIVE)
        {
            System_printf ("Error Disabling high priority accumulator for channel : %d error code: %d\n",
                          accChannelNum, result);
            return -1;
        }
    
        /* Setup the accumulator settings */
        accCfg.channel             =   accChannelNum;
        accCfg.command             =   Qmss_AccCmd_ENABLE_CHANNEL;
        accCfg.queueEnMask         =   0;
        accCfg.listAddress         =   Convert_CoreLocal2GlobalAddr((Uint32) gHiPriAccumList);
        accCfg.queMgrIndex         =   gRxQHnd;
        accCfg.maxPageEntries      =   (intThreshold + 1); /* Add an extra entry for holding the entry count */
        accCfg.timerLoadCount      =   0;
        accCfg.interruptPacingMode =   Qmss_AccPacingMode_LAST_INTERRUPT;
        accCfg.listEntrySize       =   Qmss_AccEntrySize_REG_D;
        accCfg.listCountMode       =   Qmss_AccCountMode_ENTRY_COUNT;
        accCfg.multiQueueMode      =   Qmss_AccQueueMode_SINGLE_QUEUE;
      
        /* Program the accumulator */
        if ((result = Qmss_programAccumulator (Qmss_PdspId_PDSP1, &accCfg)) != QMSS_ACC_SOK)
        {
            System_printf ("Error Programming high priority accumulator for channel : %d queue : %d error code : %d\n",
                            accCfg.channel, accCfg.queMgrIndex, result);
            return -1;
        }
    
        /* Register interrupts for the system event corresponding to the
         * accumulator channel we are using.
         */
        /* System event 48 - Accumulator Channel 0 */
        eventId     	=   48;
    
        /* Pick a interrupt vector id to use */
        vectId          =   7;
       
        /* Register our ISR handle for this event */
        EventCombiner_dispatchPlug (eventId, (EventCombiner_FuncPtr)Cpsw_RxISR, (UArg)NULL, TRUE);
    
    	/* Map the combiner's output event id (evevtId/32) to hardware interrupt 8. */
        /* The HW int 8 is slected via CM.eventGroupHwiNum[] specified at cpsw_example.cfg */
        Hwi_eventMap(vectId, 1); 
    
        /* Enable interrupt 8. */
        Hwi_enableInterrupt(vectId);    
        
        /* Open a Rx Free Descriptor Queue (Rx FDQ). 
         *
         * This queue will hold all the Rx free decriptors. These descriptors will be
         * used by the PASS CPDMA to hold data received via CPSW.
         */
        if ((gRxFreeQHnd = Qmss_queueOpen (Qmss_QueueType_STARVATION_COUNTER_QUEUE, QMSS_PARAM_NOT_SPECIFIED, &isAllocated)) < 0)
        {
            System_printf ("Error opening Rx Free descriptor queue \n");
            return -1;
        }            
        rxFreeQInfo = Qmss_getQueueNumber (gRxFreeQHnd);
    
        /* Attach some free descriptors to the Rx free queue we just opened. */
        for (i = 0; i < NUM_RX_DESC; i++)
        {
            /* Get a free descriptor from the global free queue we setup 
             * during initialization.
             */
            if ((pCppiDesc = Qmss_queuePop (gGlobalFreeQHnd)) == NULL)
            {
                break;                
            }
    
            /* The descriptor address returned from the hardware has the 
             * descriptor size appended to the address in the last 4 bits.
             *
             * To get the true descriptor size, always mask off the last 
             * 4 bits of the address.
             */
            pCppiDesc = (Ptr) ((UInt32) pCppiDesc & 0xFFFFFFF0);
    
            if ((pDataBuffer = (Ptr) Memory_alloc(NULL, 1518, 0, NULL)) == NULL)
            {
                System_printf ("Error allocating memory for Rx data buffer \n");
                break;
            }
            
            /* Populate the Rx free descriptor with the buffer we just allocated. */
            Cppi_setData (Cppi_DescType_HOST, pCppiDesc, (UInt8 *)Convert_CoreLocal2GlobalAddr((UInt32)pDataBuffer), 1518);
    
            /* Save original buffer information */
            Cppi_setOriginalBufInfo (Cppi_DescType_HOST, pCppiDesc, (UInt8 *)Convert_CoreLocal2GlobalAddr((UInt32)pDataBuffer), 1518);
    
            /* Setup the Completion queue:
             *
             * Setup the return policy for this desc to return to the free q we just
             * setup instead of the global free queue.
             */
            Cppi_setReturnQueue (Cppi_DescType_HOST, pCppiDesc, rxFreeQInfo);
    
            Cppi_setSoftwareInfo (Cppi_DescType_HOST, pCppiDesc, (UInt8 *) mySWInfo);
    
            Cppi_setPacketLen    (Cppi_DescType_HOST, pCppiDesc, 1518);
            
            /* Push descriptor to Tx free queue */
            Qmss_queuePushDescSize (gRxFreeQHnd, pCppiDesc, SIZE_HOST_DESC);           
        }        
        if (i != NUM_RX_DESC)
        {
            System_printf ("Error allocating Rx free descriptors \n");
            return -1;
        }
    
        /* Setup a Rx Flow.
         *
         * A Rx flow encapsulates all relevant data properties that CPDMA would
         * have to know in order to succefully receive data.
         */
        /* Initialize the flow configuration */
        memset (&rxFlowCfg, 0, sizeof(Cppi_RxFlowCfg));
    
        /* Let CPPI pick the next available flow */
        rxFlowCfg.flowIdNum             =   CPPI_PARAM_NOT_SPECIFIED;    
    
        rxFlowCfg.rx_dest_qmgr          =   rxQInfo.qMgr;    
        rxFlowCfg.rx_dest_qnum          =   rxQInfo.qNum;  
        rxFlowCfg.rx_desc_type          =   Cppi_DescType_HOST; 
    
        rxFlowCfg.rx_ps_location        =   Cppi_PSLoc_PS_IN_DESC;  
        rxFlowCfg.rx_psinfo_present     =   1;    /* Enable PS info */
        
        rxFlowCfg.rx_error_handling     =   0;    /* Drop the packet, do not retry on starvation by default */       
        rxFlowCfg.rx_einfo_present      =   1;    /* EPIB info present */       
        
        rxFlowCfg.rx_dest_tag_lo_sel    =   0;    /* Disable tagging */
        rxFlowCfg.rx_dest_tag_hi_sel    =   0;    
        rxFlowCfg.rx_src_tag_lo_sel     =   0;    
        rxFlowCfg.rx_src_tag_hi_sel     =   0;    
    
        rxFlowCfg.rx_size_thresh0_en    =   0;    /* By default, we disable Rx Thresholds */
        rxFlowCfg.rx_size_thresh1_en    =   0;    /* By default, we disable Rx Thresholds */
        rxFlowCfg.rx_size_thresh2_en    =   0;    /* By default, we disable Rx Thresholds */
        rxFlowCfg.rx_size_thresh0       =   0x0;
        rxFlowCfg.rx_size_thresh1       =   0x0;
        rxFlowCfg.rx_size_thresh2       =   0x0;
    
        rxFlowCfg.rx_fdq0_sz0_qmgr      =   rxFreeQInfo.qMgr; /* Setup the Receive free queue for the flow */
        rxFlowCfg.rx_fdq0_sz0_qnum      =   rxFreeQInfo.qNum;    
        rxFlowCfg.rx_fdq0_sz1_qnum      =   0x0; 
        rxFlowCfg.rx_fdq0_sz1_qmgr      =   0x0;
        rxFlowCfg.rx_fdq0_sz2_qnum      =   0x0;
        rxFlowCfg.rx_fdq0_sz2_qmgr      =   0x0;
        rxFlowCfg.rx_fdq0_sz3_qnum      =   0x0;
        rxFlowCfg.rx_fdq0_sz3_qmgr      =   0x0;
    
        rxFlowCfg.rx_fdq1_qnum          =   rxFreeQInfo.qNum;  /* Use the Rx Queue to pick descriptors */
        rxFlowCfg.rx_fdq1_qmgr          =   rxFreeQInfo.qMgr;
        rxFlowCfg.rx_fdq2_qnum          =   rxFreeQInfo.qNum;  /* Use the Rx Queue to pick descriptors */
        rxFlowCfg.rx_fdq2_qmgr          =   rxFreeQInfo.qMgr;
        rxFlowCfg.rx_fdq3_qnum          =   rxFreeQInfo.qNum;  /* Use the Rx Queue to pick descriptors */
        rxFlowCfg.rx_fdq3_qmgr          =   rxFreeQInfo.qMgr;
    
        /* Configure the Rx flow */
        if ((gRxFlowHnd = Cppi_configureRxFlow (gCpdmaHnd, &rxFlowCfg, &isAllocated)) == NULL)
        {
            System_printf ("Error configuring Rx flow \n");
            return -1;
        }
    
        /* All done with Rx configuration. Return success. */
        return 0;
    }
    // **********************************************************************
    #define htons(X) X
    Uint32 chksum
    (
        Uint8  *buf,   /* memory buffer to calculate sum over  */
        Uint32 len     /* buffer's length                      */
    )
    {
        Uint16 word16;
        Uint32 sum = 0;
        Uint16 i;
    
        /* make 16 bit words out of every two adjacent  */
        /* 8 bit words in the packet and add them up    */
    	for ( i = 0; i < (len & ~0x1); i = i + 2 )
        {
    		word16 = ((buf[i] << 8) & 0xFF00) + (buf[i+1] & 0xFF);
    		sum = sum + word16;
    	}
    
        /* add up any odd byte */
        if (len & 0x01)
        {
            sum += (Uint16) ((buf[len-1] << 8) & 0xFF00);
        }
    
        return sum;
    }
     // *****************************************************************
    Uint16 inet_chksum
    (
        Uint8  *buf,   /* memory buffer to calculate sum over  */
        Uint32 len     /* buffer's length                      */
    )
    {
        Uint32 acc;
    
        acc = chksum(buf, len);
        while(acc >> 16)
            acc = (acc & 0xffff) + (acc >> 16);
        acc = ~acc;
        return (Uint16) acc;
    }
    //***********************************************************************
    Uint16 inet_chksum_pseudo
    (
        Uint32 src,    /* IP source addres, network byte order         */
        Uint32 dst,    /* IP destination addres, network byte order    */
    	Uint8  proto,  /* protocol field, host byte order              */
        Uint16 len,    /* length of data to sum                        */
        Uint8  *buf    /* data buffer to sum                           */
    )
    {
        Uint32 acc;
    
        acc  = chksum(buf, len);
        //acc += ((src >>  0) & 0xffff);
        //acc += ((src >> 16) & 0xffff);
        //acc += ((dst >>  0) & 0xffff);
        //acc += ((dst >> 16) & 0xffff);
        acc += htons((Uint16)((src >>  0) & 0xffff));
        acc += htons((Uint16)((src >> 16) & 0xffff));
        acc += htons((Uint16)((dst >>  0) & 0xffff));
        acc += htons((Uint16)((dst >> 16) & 0xffff));
        //acc += (u_int32) htons((u_int16) proto);
        //acc += (u_int32) htons(len);
        acc += (Uint32) (proto);
        acc += (Uint32) (len);
    
        while(acc >> 16)
            acc = (acc & 0xffff) + (acc >> 16);
        acc = ~acc;
    
        return (Uint16) acc;
    }
    //***************************************************************************
    Uint8 * udp_compose
    (
        Uint8  src_mac[6], // source MAC address, network byte order
        Uint8  dst_mac[6], // destination MAC address, network byte order
        Uint32 src_ip,     // source IP address, host byte order
        Uint32 dst_ip,     // destination IP address, host byte order
        Uint16 src_port,   // UDP source port
        Uint16 dst_port,   // UDP destination port
        Uint8  *pld,       // payload of the packet
        Uint16 pld_len,    // quantity of octets in the payload
        Uint8  *udp_pkt    // pointer to allocated buffer to store the UDP packet. Allocated length shall be at least 42+pld_len, but not less than 60 bytes since minimal length of Ethernet frame shall be 64 bytes including 4-bytes FCS (CRC). The FCS will be added later by HW
    )
    {
    //    Ethernet frame structure for UDP packet
    //   +-----------------+-----------------+------------+-----------------------+----------------------------+
    //   | Ethernet header | Internet header | UDP header | Payload (<1472 octets)| Frame check sequence (CRC) |
    //   +-----------------+-----------------+------------+-----------------------+----------------------------+
    //   <--- 14 octets --> <-- 20 octets --> <-8 octets-> <-- 18...1472 octets -> <------- 4 octets ---------->
    //   <----------------------------------------- <1518 octets ---------------------------------------------->
    
    //   Ethernet header format
    //   +-----------------+----------------+-----------+
    //   | MAC destination |   MAC source   | Ethertype |
    //   |    6 octets     |    6 octets    | 2 octets  |
    //   +-----------------+----------------+-----------+
    
    //    Internet header Format
    //    0                   1                   2                   3
    //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   |Version|  IHL  |Type of Service|          Total Length         |
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   |         Identification        |Flags|      Fragment Offset    |
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   |  Time to Live |    Protocol   |         Header Checksum       |
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   |                       Source Address                          |
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   |                    Destination Address                        |
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    //   |                    Options                    |    Padding    |
    //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
    //   UDP header format
    //    0      7 8     15 16    23 24    31
    //   +--------+--------+--------+--------+
    //   |     Source      |   Destination   |
    //   |      Port       |      Port       |
    //   +--------+--------+--------+--------+
    //   |     Length      |    Checksum     |
    //   +--------+--------+--------+--------+
    
        Uint16 len, chk;
    
        memcpy(udp_pkt+42, pld, pld_len);   // copy payload. The payload is located after Ethernet, Internet and UDP headers (42 bytes)
        if(pld_len < 18)    // add padding zeros
            memset(udp_pkt+42+pld_len, 0, 18 - pld_len);
    
        memset(udp_pkt, 0, 42); // initialize headers to all zeroes
    
        // --> Ethernet header
        memcpy(udp_pkt, dst_mac, 6);
        memcpy(udp_pkt+6, src_mac, 6);
        udp_pkt[12] = 0x08; udp_pkt[13] = 0x00;  // Ethertype = IPv4:
        // <-- Ethernet header
        // --> Internet header
        udp_pkt[14] = 0x45; // Current version is 4. Internet Header Length is the length of the internet header in 32 bit words, and thus points to the beginning of the data.  Note that the minimum value for a correct header is 5.
        udp_pkt[15] = 0x00; // see http://tools.ietf.org/html/rfc760
        len = pld_len + 20 + 8; // pld_len + internet header length + UDP header length
        udp_pkt[16] = (Uint8)(len>>8);
        udp_pkt[17] = (Uint8)(len>>0);
        // IP ID, flags, fragment offset are zeros, so skipped
        udp_pkt[22] = 0x05; //Time to Live: This field indicates the maximum time the datagram is allowed to remain the internet system.  If this field contains the value zero, then the datagram should be destroyed.  This field is modified in internet header processing.  The time is measured in units of seconds.  The intention is to cause undeliverable datagrams to be discarded.
        udp_pkt[23] = 17; // protocol (UDP)
        // field of checksum will be filled below. At this moment (before checksum computation) the field shall be zero.
        udp_pkt[26] = (Uint8)(src_ip>>24);
        udp_pkt[27] = (Uint8)(src_ip>>16);
        udp_pkt[28] = (Uint8)(src_ip>>8);
        udp_pkt[29] = (Uint8)(src_ip>>0);
        udp_pkt[30] = (Uint8)(dst_ip>>24);
        udp_pkt[31] = (Uint8)(dst_ip>>16);
        udp_pkt[32] = (Uint8)(dst_ip>>8);
        udp_pkt[33] = (Uint8)(dst_ip>>0);
        chk = inet_chksum(udp_pkt + 14, 20);
        udp_pkt[24] = (Uint8)(chk>>8);
        udp_pkt[25] = (Uint8)(chk>>0);
        // <-- Internet header
        // --> UDP header
        udp_pkt[34] = (Uint8)(src_port>>8);
        udp_pkt[35] = (Uint8)(src_port>>0);
        udp_pkt[36] = (Uint8)(dst_port>>8);
        udp_pkt[37] = (Uint8)(dst_port>>0);
        len = pld_len + 8; // pld_len + UDP header length
        udp_pkt[38] = (Uint8)(len>>8);
        udp_pkt[39] = (Uint8)(len>>0);
        chk = inet_chksum_pseudo(src_ip, dst_ip, 17, len, udp_pkt + 34);
        if(0 == chk)
            chk = ~0;
        udp_pkt[40] = (Uint8)(chk>>8);
        udp_pkt[41] = (Uint8)(chk>>0);
        // <-- UDP header
        // Frame check sequence will be added by HW
        return udp_pkt;
    }
    //**********************************************************
    Uint32 inetaddr
    (
        const char *ip  // IP address string
    )
    {
        Uint32 ipui = 0;
        int j = 0;
        while(j < 4) {
            Uint16 b;
            char oct[4];
            Uint8 i = 0;
            while((i < 3) && (ip[0] != '.') && (ip[0])) {
                oct[i] = ip[0];
                ++i;
                ++ip;
            }
            if((!i) || (i > 3)) return ipui;
            oct[i] = 0;
            b = (Uint16) strtol(oct, NULL, 10);
            ipui |= b << ((3-j)*8);
            ++ip;
            ++j;
        }
        return ipui;
    }
    /** ============================================================================
     *   @n@b SendPacket
     *
     *   @b Description
     *   @n This API is called to actually send out data onto wire using ethernet.
     *      On success, this API increments a global Tx counter to indicate the same.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return    Int32
     *              -1      -   Error
     *              0       -   Success
     * =============================================================================
     */
    Int32 SendPacket (Void)
    {
        Cppi_Desc*      pCppiDesc;
        UInt32          dataBufferSize,i,j,src_ip,dst_ip;
        char            psFlags = (cpswSimTest)?pa_EMAC_PORT_0:pa_EMAC_PORT_1;
        Uint8           pld[1472];
        #define DST_IP      "192.168.1.10"		// User PC
        #define SRC_IP      "192.168.1.1"	// MTP300A
        Uint8           dst_mac[6] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
        Uint8           src_mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
    
        src_ip = inetaddr(SRC_IP);
        dst_ip = inetaddr(DST_IP);
        /* Get a free descriptor from the global free queue we setup 
         * during initialization.
         */
        if ((pCppiDesc = Qmss_queuePop (gTxFreeQHnd)) == NULL)
        {
            System_printf ("No Tx free descriptor. Cant run send/rcv test \n");
            return -1;
        }
    
        /* The descriptor address returned from the hardware has the 
         * descriptor size appended to the address in the last 4 bits.
         *
         * To get the true descriptor size, always mask off the last 
         * 4 bits of the address.
         */
    
        pCppiDesc = (Ptr) ((UInt32) pCppiDesc & 0xFFFFFFF0);    
    
        // creating any payload
        for (i=0,j=0;i<1472;i++,j++) {
        	pld[i] = j;
        	if (i>=255)
        		j = 0;
        }
        udp_compose(src_mac,dst_mac,src_ip,dst_ip,4660,22136,pld,1472,pktMatch);
        //dataBufferSize  =   sizeof (pktMatch);
        dataBufferSize  =   sizeof (pld) + 42;
        Cppi_setData (  Cppi_DescType_HOST, 
                        (Cppi_Desc *) pCppiDesc, 
                        (UInt8 *) Convert_CoreLocal2GlobalAddr((UInt32)pktMatch), 
                        dataBufferSize
                     );
        Cppi_setPacketLen (Cppi_DescType_HOST, (Cppi_Desc *)pCppiDesc, dataBufferSize);
        
        if (cpswLpbkMode != CPSW_LOOPBACK_NONE)
        {
            /* Force the packet to the specific EMAC port if loopback is enabled */
            Cppi_setPSFlags(Cppi_DescType_HOST, (Cppi_Desc *)pCppiDesc, psFlags);
        }
        else
        {
            Cppi_setPSFlags(Cppi_DescType_HOST, (Cppi_Desc *)pCppiDesc, 0);
        }
      
        /* Send the packet out the mac. It will loop back to PA if the mac/switch 
         * have been configured properly 
         */  
        Qmss_queuePush (gPaTxQHnd[8], pCppiDesc, dataBufferSize, SIZE_HOST_DESC, Qmss_Location_TAIL);
    
        /* Increment the application transmit counter */
        gTxCounter ++;
    
        /* Give some time for the PA to process the packet */
        //CycleDelay (10000);
    
        return 0; 
    }
    
    /** ============================================================================
     *   @n@b Cpsw_RxISR
     *
     *   @b Description
     *   @n This API is the example application's High Priority Accumulation interrupt
     *      Service Handler (ISR). This API is called in interrupt context. This API
     *      fetches the Received packet (descriptor) from the accumulator list and
     *      verifies the data received to ensure that it is correct. On success, 
     *      this API recycles the Rx descriptor back to Rx free queue for use again.
     *      This API processes the Ping and Pong accumulator lists alternatively.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return    
     *   @n None
     * =============================================================================
     */
    Void Cpsw_RxISR (Void)
    {
        Cppi_Desc*                  pCppiDesc;        
        UInt32                     	count, i;
        
        /* Process ISR. 
         *
         * Get the number of entries in accumulator list. 
    	 * The hardware enqueues data alternatively to Ping/Pong buffer lists in
         * the accumulator. Hence, we need to track which list (Ping/Pong)
         * we serviced the last time and accordingly process the other one
         * this time around.
         */ 
         if (!gIsPingListUsed)
         {
    		/* Serviced Pong list last time. So read off the Ping list now */     	
        	count   =   gHiPriAccumList[0];     	
         }
         else
         {
    		/* Serviced Ping list last time. So read off the Pong list now */     	
        	count   =   gHiPriAccumList[RX_INT_THRESHOLD + 1];     	
         }
        
        /* Process all the Results received 
         *
         * Skip the first entry in the list that contains the 
         * entry count and proceed processing results.
         */
        for (i = 1; i <= count; i ++)
        {
            /* Get the result descriptor.
             *
             * The hardware enqueues data alternatively to Ping/Pong buffer lists in
             * the accumulator. Hence, we need to track which list (Ping/Pong)
             * we serviced the last time and accordingly process the other one
             * this time around.
             */
            if (!gIsPingListUsed)
            {
                /* Serviced Pong list last time. So read off the Ping list now */                
                pCppiDesc   =   (Cppi_Desc *) gHiPriAccumList [i];
            }
            else
            {
                /* Serviced Ping list last time. So read off the Pong list now 
                 *
                 * Skip over Ping list length to arrive at Pong list start.
                 */                
                pCppiDesc   =   (Cppi_Desc *) gHiPriAccumList [i + RX_INT_THRESHOLD + 1];
            }
    
            /* Descriptor size appended to the address in the last 4 bits.
             *
             * To get the true descriptor size, always mask off the last 
             * 4 bits of the address.
             */
    	    pCppiDesc = (Ptr) ((UInt32) pCppiDesc & 0xFFFFFFF0);    
    
            VerifyPacket (pCppiDesc);
        }
    
        /* Clear the accumulator list and save whether we used Ping/Pong
         * list information for next time around.
         */
        if (!gIsPingListUsed)
        {
            /* Just processed Ping list */            
            gIsPingListUsed  =   1;
        
            /* Clear the accumulator list after processing */
            memset ((Void *) &gHiPriAccumList [0], 0, sizeof (UInt32) * (RX_INT_THRESHOLD + 1));
        }
        else
        {
            /* Just processed Pong list */            
            gIsPingListUsed  =   0;
    
            /* Clear the accumulator list after processing */
            memset ((Void *) &gHiPriAccumList[RX_INT_THRESHOLD + 1], 0, sizeof (UInt32) * (RX_INT_THRESHOLD + 1));
        }
    
    	/* Clear INTD */
    	Qmss_ackInterrupt(PA_ACC_CHANNEL_NUM, 1);
    	Qmss_setEoiVector(Qmss_IntdInterruptType_HIGH, PA_ACC_CHANNEL_NUM);
    	
        /* Done processing interrupt. Return */
        return;
    }
    
    /** ============================================================================
     *   @n@b VerifyPacket
     *
     *   @b Description
     *   @n This API verifies a packet received against the expected data and 
     *      returns 0 to inidcate success and -1 to indicate a mismatch.
     *
     *   @param[in]  
     *   @n pCppiDesc           Packet descriptor received.
     * 
     *   @return    Int32
     *              -1      -   Error
     *              0       -   Success
     * =============================================================================
     */
    Int32 VerifyPacket (Cppi_Desc* pCppiDesc)
    {
    	Cppi_HostDesc               *pHostDesc;
    	UInt8                       *pDataBuffer;
    	Int32                       i;
    	UInt32		      	        infoLen;
    	pasahoLongInfo_t 	        *pinfo;
        UInt8                       portNum;       
    	
        pHostDesc = (Cppi_HostDesc *)pCppiDesc;
           
        /* Verify the application software info we received is same
         * as what we had sent earlier.
         */
        if (pHostDesc->softwareInfo0 != 0xaaaaaaaa)  
        {
            System_printf ("VerifyPacket: Found an entry in receive queue with swinfo0 = 0x%08x, expected 0x%08x\n", 
                            pHostDesc->softwareInfo0, 0xaaaaaaaa);
                       
            pHostDesc->buffLen = pHostDesc->origBufferLen;
            Qmss_queuePush (gRxFreeQHnd, (Ptr)pHostDesc, pHostDesc->buffLen, SIZE_HOST_DESC, Qmss_Location_TAIL);
              
            return -1;
        }
        
    	/* Get the parse information, make sure there is an L4 offset */
    	if (Cppi_getPSData (Cppi_DescType_HOST, Cppi_PSLoc_PS_IN_DESC, (Cppi_Desc *)pHostDesc, (UInt8 **)&pinfo, &infoLen) != CPPI_SOK)  {
    		System_printf ("VerifyPacket: Error getting control info from received data packet\n");
    		return (-1);
    	}
        else if(!cpswSimTest)
        {
            /* Verify the input port number */
            portNum = PASAHO_LINFO_READ_INPORT(pinfo);
            
            if (portNum != pa_EMAC_PORT_1)
            {
    		    System_printf ("VerifyPacket: receive packet from unexpected EMAC PORT %d (expected %d)\n", portNum - 1, pa_EMAC_PORT_1 - 1);
                System_flush();
            }
        }
        
        /* Verify the packet matches what we had sent */
        pDataBuffer = (UInt8 *) pHostDesc->buffPtr;
        for (i = 42; i < sizeof (pktMatch); i++)  
        {
            if (pktMatch[i] != pDataBuffer[i])  
            {
                System_printf ("VerifyPacket: Byte %d expected 0x%02x, found 0x%02x\n", i, pktMatch[i], pDataBuffer[i]);
                System_flush();
    
                /* Free the packet back to the Rx FDQ */
                pHostDesc->buffLen = pHostDesc->origBufferLen;
                Qmss_queuePush (gRxFreeQHnd, (Ptr)pHostDesc, pHostDesc->buffLen, SIZE_HOST_DESC, Qmss_Location_TAIL);
                return -1;
            }
        }
            
        //System_printf ("Packet Received Verified Successfully!\n");
           
        /* Increment Rx counter to indicate the number of successfully
         * received packets by the example app.
         */
        gRxCounter ++;
    
        /* Reset the buffer lenght and put the descriptor back on the free queue */      
        pHostDesc->buffLen = pHostDesc->origBufferLen;
        Qmss_queuePush (gRxFreeQHnd, (Ptr)pHostDesc, pHostDesc->buffLen, SIZE_HOST_DESC, Qmss_Location_TAIL);
    
        /* Verify packet done. Return success. */
    	return 0;
    }
    	
    

    /**  
     * @file cpsw_singlecore.c
     *
     * @brief 
     *  Example to illustrate the usage of EMAC CPSW3G switch using CPPI, QMSS
     * 	low level drivers and CSL.
     *
     * 	This example application does the following:
     * 	    (1) Initializes:
     * 	            (a) Queue Manager (QM) Subsystem 
     * 	            (b) Packet Accelerator (PA) CPPI DMA 
     * 	            (c) Ethernet Subsystem (Ethernet switch + SGMII + MDIO)
     * 	            (d) PA Subsystem + PDSP
     *
     * 	    (2) Sets up the CPPI descriptors and Queues required for sending and
     * 	        receiving data using Ethernet.
     * 	            (a) Uses Host descriptors
     * 	            (b) Uses High Priority Accumulation interrupts
     *
     * 	    (3) Sets up the example application's configuration (MAC address
     * 	        it uses to send/recv data; IP address and port number it's listening
     * 	        on) in PA Subsystem so as to enable the PASS to forward all packets
     * 	        matching this configuration onto the application for processing.
     * 	            (a) Switch MAC address configured   =   0x10:0x11:0x12:0x13:0x14:0x15
     * 	            (b) Example's IP address            =   192.168.1.10
     * 	            (c) Example App's listening port    =   0x5678
     *
     * 	    (4) Sends packets onto wire 
     * 	        (constructed manually in code here with following settings):
     * 	            (a) Source MAC      =   0x00:0x01:0x02:0x03:0x04:0x05
     * 	                Destination MAC =   0x10:0x11:0x12:0x13:0x14:0x15
     *              (b) Source IP       =   192.168.1.1
     *                  Destination IP  =   192.168.1.10
     *              (c) Source Port     =   0x1234
     *                  Destination Port=   0x5678
     *              (d) Payload Data (80 bytes)
     *
     *          The packets sent by the application are sent onto wire and 
     *          since the destination MAC on the packet is the Ethernet Switch 
     *          MAC address, the packets are received by simulator and passed 
     *          back up to the example application for processing.
     *      
     *      (5) Application receives all packets using QM High priority interrupt
     *          registered; Validates received packet against data sent.
     *
     *  Example application Setup:
     *
     *          PC Running Simulator using CCS connected to a
     *          Switch/Hub. You could put another PC on the Hub to observe packets 
     *          being sent onto wire. 
     *
     *          Please consult the Readme.txt packaged with the example to 
     *          setup the CCS simulator configuration required to run this example 
     *          succesfully.
     *
     *  \par
     *  ============================================================================
     *  @n   (C) Copyright 2009, Texas Instruments, Inc.
     * 
     *  Redistribution and use in source and binary forms, with or without 
     *  modification, are permitted provided that the following conditions 
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright 
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the 
     *    documentation and/or other materials provided with the   
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
    */
    #include <cpsw_singlecore.h>
    #include <stdio.h>
    #include <ti/csl/cslr_device.h>
    #include <ti/csl/csl_psc.h>
    #include <ti/csl/csl_pscAux.h>
    
    
    /**************************************************************
    ************************** DEFINITIONS ************************
    ***************************************************************/
    /* Number of packets to be used for testing the example. */
    #define                     MAX_NUM_PACKETS                         10u
    
    /* Counters to track number of packets sent/received by this application */
    extern volatile UInt32				gRxCounter, gTxCounter;
    
    /*
     * Default test configuration for the silicon
     *
     * To run test at the CCS simulator
     *    cpswSimTest = 1
     *    cpswLpbkMode = CPSW_LOOPBACK_EXTERNAL
     */
    #ifdef  SIMULATOR_SUPPORT
    Int cpswSimTest = 1;
    Int cpswLpbkMode = CPSW_LOOPBACK_EXTERNAL;
    #else
    Int cpswSimTest = 0;
    Int cpswLpbkMode = CPSW_LOOPBACK_NONE;//CPSW_LOOPBACK_INTERNAL;
    #endif
    
    void mdebugHaltPdsp (Int pdspNum);
    volatile Int mdebugWait = 1;
    
    /**************************************************************
    **************** EXAMPLE APP FUNCTIONS ************************
    ***************************************************************/
    
    /** ============================================================================
     *   @n@b Cpsw_SingleCoreApp
     *
     *   @b Description
     *   @n Example application that sets up the application, sends, receives
     *      data.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return
     *   @n None
     *
     * =============================================================================
     */
    Void Cpsw_SingleCoreApp (Void)
    {
    	extern void view_ale_table(void);
    	Int32			i;
    	int				ct_show_ale = 0;
    
        System_printf ("**************************************************\n");
        System_printf ("******* Ethernet Single Core Example Start *******\n");
        System_printf ("**************************************************\n");
    
        /* Init internal cycle counter */
        TSCL = 1;  
    
        /* Initialize the components required to run the example:
         *  (1) QMSS
         *  (2) CPPI
         *  (3) Ethernet switch subsystem + MDIO + SGMII
         */
        /* Initialize QMSS */
        if (Init_Qmss () != 0)
        {
            System_printf ("QMSS init failed \n");
            BIOS_exit (-1);
        }
        else
        {
            System_printf ("QMSS successfully initialized \n");            
        }
    
        /* Initialize CPPI */
        if (Init_Cppi () != 0)
        {
            System_printf ("CPPI init failed \n");
            BIOS_exit (-1);
        }
        else
        {
            System_printf ("CPPI successfully initialized \n");            
        }
    
        /* Init PA LLD */
        if (Init_PASS () != 0)
        {
            System_printf ("PASS init failed \n");
            BIOS_exit (-1);
        }
        else
        {
            System_printf ("PASS successfully initialized \n");            
        }
    
        /* Initialize the CPSW switch */
        if (Init_Cpsw () != 0)
        {
            System_printf ("Ethernet subsystem init failed \n");
            BIOS_exit (-1);
        }
        else
        {
            System_printf ("Ethernet subsystem successfully initialized \n");            
        }
        
        /* Setup Tx */
        if (Setup_Tx () != 0)
        {
            System_printf ("Tx setup failed \n");
            BIOS_exit (-1);
        }
        else
        {
            System_printf ("Tx setup successfully done \n");            
        }
    
        /* Setup Rx */
        if (Setup_Rx () != 0)
        {
            System_printf ("Rx setup failed \n");
            BIOS_exit (-1);
        }
        else
        {
            System_printf ("Rx setup successfully done \n");            
        }
    
        /* Setup PA */
        if (Setup_PASS () != 0)
        {
            System_printf ("PASS setup failed \n");
            BIOS_exit (-1);
        }
        else
        {
            System_printf ("PASS setup successfully done \n");            
        }
        
        if (!cpswSimTest)
        {
            System_printf("Following is the ALE table before transmits.\n");
            view_ale_table();	// Added by Atsushi
        }
        
        /* Run some data through and verify transfer worked */
        System_printf ("Packet Transmission Start ... \n");
        for (i = 0; i < MAX_NUM_PACKETS; i ++)
        {
            if (SendPacket () != 0)
            {
                System_printf ("Packet %d send failed \n", i);
                BIOS_exit (-1);
            }
        }
        
        if (!cpswSimTest)
        {
    	    System_printf("Following is the ALE table after transmits.\n");
            view_ale_table();	
        }
        
        /* Wait until all packet reception is done */
       	System_printf ("Packet Transmission Done.\nWait for all packets to be Received ... \n");
    
    //    while (gRxCounter != gTxCounter) {
    //    	System_printf("Received %d packets so far...\n", gRxCounter);
    //        CycleDelay (10000);
    //
    //        if (!cpswSimTest)
    //        {
    //    	    if (++ ct_show_ale >= 10) {
    //    		    view_ale_table();
    //    		    ct_show_ale = 0;
    //    	    }
    //        }
    //    }
        
        System_printf ("Packets Sent\t\t=\t%d \nPackets Received\t=\t%d \nExample Done! \n", gTxCounter,  gRxCounter);
      
        System_printf ("**************************************************\n");
        System_printf ("******** Ethernet Single Core Example End ********\n");
        System_printf ("**************************************************\n");
    
        /* Example application done. Return success */
        BIOS_exit (0);
        
    }
    
    /***************************************************************************************
     * FUNCTION PURPOSE: Power up PA subsystem
     ***************************************************************************************
     * DESCRIPTION: this function powers up the PA subsystem domains
     ***************************************************************************************/
    void passPowerUp (void)
    {
    
        /* PASS power domain is turned OFF by default. It needs to be turned on before doing any 
         * PASS device register access. This not required for the simulator. */
    
        /* Set PASS Power domain to ON */        
        CSL_PSC_enablePowerDomain (CSL_PSC_PD_PASS);
    
        /* Enable the clocks for PASS modules */
        CSL_PSC_setModuleNextState (CSL_PSC_LPSC_PKTPROC, PSC_MODSTATE_ENABLE);
        CSL_PSC_setModuleNextState (CSL_PSC_LPSC_CPGMAC,  PSC_MODSTATE_ENABLE);
        CSL_PSC_setModuleNextState (CSL_PSC_LPSC_Crypto,  PSC_MODSTATE_ENABLE);
    
        /* Start the state transition */
        CSL_PSC_startStateTransition (CSL_PSC_PD_PASS);
    
        /* Wait until the state transition process is completed. */
        while (!CSL_PSC_isStateTransitionDone (CSL_PSC_PD_PASS));
    }
    
    /** ============================================================================
     *   @n@b main
     *
     *   @b Description
     *   @n Entry point for single core example application.
     *
     *   @param[in]  
     *   @n None
     * 
     *   @return
     *   @n None
     * =============================================================================
     */
    Int32 main (Void)
    {
        Task_Params                	cpswTaskParams;
        
        if (!cpswSimTest)
        {
          passPowerUp();
        }
        
        /* Initialize the task params */
        Task_Params_init(&cpswTaskParams);
    
        /* Create the CPSW single core example task */
        Task_create((Task_FuncPtr)&Cpsw_SingleCoreApp, &cpswTaskParams, NULL);
    
        /* Start the BIOS Task scheduler */
    	BIOS_start ();
    
    	return 0;	
    }
    

  • Pavlo,

    For the wrong CRC issue:

    • you mentioned that can't be reproduced by changing TI reference project
    • the checksum is generated by udp_compose/inet_chksum/inet_chksum_pseudo functions, it is pure SW application to generate it
    • So this is not a real issue for PA

    For lost packet without delay issue:

    • This is known issue and need to track the TxQ[8] doesn't get full
    • Do you observe the same? Or there are still packet losses even queue is not full?

    Regards, Eric

  • lding said:
    This is known issue and need to track the TxQ[8] doesn't get full

    Eric, with quoted above, do you mean that there always should be some free room in TxQ, never allow the queue to fill up even if it does not over flow, just fill up? Could you please point us to some more reading about that? Thanks.

  • Eric,

    I'am sorry for a delay, but I need to figure everything out for myself before posting something.

    I have found the problem in our software. Wrong checksum generation was caused by free of  software pre allocated queue right after Qmss_queuePush () function. I think it's because we are using Host descriptors. The first question: Is it possible to solve that problem with using of Monolithic descriptors instead?

    Now we have created some static two dimension buffers, where one dimension is number of TX descriptors and second one is Ethernet frame length (1518). So when the packet is ready it copied into  there "RX" buffers and they are specified as source for Cppi_setData () and etc. functions. Also I now we are performing push only if Qmss_getQueueEntryCount(gPaTxQHnd[8]) !=0. So at first I thought that queue overflow is impossible.

    But then after some experimentation I have found out that after push getQueueEntryCount becomes 1,but at the same moment when PA take the descriptor from the queue it goes down to 0. And if we make free when getQueueEntryCount == 0 we will still had a wrong checksum generation. On Debug mode with 0 optimization and Ethernet transfer speed ~320 Mbps everything is ok and no data loss is observed. But when we go to release and speed rise up to ~450-500 Mbps the same problems happens (wrong checksum generation). Second question: does PA has it's own internal pipeline or queue which can overflow?

    And Third one: Please could you share some link on the known issue about  TxQ[8] queue overflow?

    Best Regards,

    Pavlo!