4 Replies Latest reply on Aug 3, 2018 12:29 PM by алинабатура

    Cypress CYUSB3KIT-003 USB3 device crash

    алинабатура

      Hello,

      Maybe one of you has encountered similar problems or can give some advice on this topic. Please see the problem description below:

       

      Preliminary System Setup:

       

      We use the Cypress CYUSB3KIT-003 USB3 device to stream a bigger amount of data from an FPGA capture card to a PC via USB3. The data streamed is composed within the FPGA to a BGR video pixel stream in HD resolution. On the PC side we have a Windows application which uses the Cypress API to access the USB device.

       

      The PC application uses the CCyUSBEndPoint and the CCyUSBDevice API classes to ‘open’ a USB end point.

       

       

      Our Problem:

       

      During a session it possible that the USB device does not retrieve any new data from the FPGA capturer going to a time out. This could happen several times in a short time period. This causes either the application waiting for a transfer to be done or the USB driver crashes. After such a crash the device has to be reseted by pulling the USB connector to the PC.

       

      The following approach for the communication with the USB device via the driver is used:

       

      1. USB initialize:

         1.1 Open usb device and checks it, if it does not satisfy the conditions, then disconnects it and try to connect again. If a video stream disconnect signal appears, stop trying to connect.

         1.2 Create an array of empty buffers and a pointer to the OVERLAPPED structure and reset them, start the transfer.

      2. Data transfer cycle:

         2.1 Wait for the end of the transfer of the i-th element.

         2.2 Finish the transfer of the element and save its status.

         2.3 Status handling:

            2.3.1 If an error is received at the end of the transfer or the overflow counter is greater than 8:

               2.3.1.1 Clear all information and delete the buffer pointers.

               2.3.1.2 USB initialize[1]

            2.3.2 Process the received data, save it and restart the transfer[2].

            2.3.3 Increment the transfer counter, to go to the next pointer in the OVERLAPPED structur

        • 1. Re: Cypress CYUSB3KIT-003 USB3 device crash
          abga

          Hi,

           

          You can set the timeout value in Xferdata API greater than the waiting time so that it will never time out. Cypress strongly recommend to use UVC class application to stream video. Could you please tell me why you are using a custom application to stream video.

           

          Thanks & regards

          Abhinav

          • 2. Re: Cypress CYUSB3KIT-003 USB3 device crash
            алинабатура

            Thank you for your input. We are going to investigate if the video streaming class is feasible for our use case and “special” video signal. Also we are going to try out the idea with an increased timeout setting.

             

            We also request a little bit of help with our current solution. What are we doing wrong? Is there an “obvious” mistake we oversee?

            What we have noticed is that the “WaitForXfer“ method was finished but always return false. This causes the program to hang in an endless loop or crashing.

             

             

            Further help is much appreciated.

            • 3. Re: Cypress CYUSB3KIT-003 USB3 device crash
              abga

              Hi,

               

              Please mention what kind of transfer (synchronous or asynch) you are performing? If possible please share your application code.

               

              Thanks & Regards

              Abhinav

              • 4. Re: Cypress CYUSB3KIT-003 USB3 device crash
                алинабатура

                Hi,

                 

                thanks for your replay. Yes we can provide the important parts of the code here and explain how it is glued together.

                 

                This part of the application is “glued” together via a Qt queue with which the data is transferred to another thread. The USB code is running in its own thread which’s run method we see below:

                void StreamProducer::run()
                {
                m_pClient = std::make_unique<RW::VGR::FG_USB::FX3VideoStreamer>(m_Logger);
                
                
                if (m_pClient->iAppUsbInit() != FG_USB::AppReturnCode::APP_SUCCESS)
                {
                m_Logger->error("StreamProducer::run: Initialise failed!");
                return;
                }
                m_bBreak = false;
                FG_USB::FramebufferConfig_t fbConfig;
                fbConfig.pingMat = std::make_shared<cv::Mat>(ROWS, COLS, CV_8UC3);
                fbConfig.pongMat = std::make_shared<cv::Mat>(ROWS, COLS, CV_8UC3);
                
                
                while (!m_bBreak)
                {
                if (m_pClient->bAppUsbStreamRx(&fbConfig))
                {
                std::shared_ptr<cv::Mat> pgMat = fbConfig.ping_pong ? fbConfig.pingMat : fbConfig.pongMat;
                
                
                m_qMutex->lock();
                m_qQueue->enqueue(pgMat);
                m_qMutex->unlock();
                }
                }
                }
                

                 

                Please note how the USB device is initialized (iAppUsbInit()) and then bAppUsbStreamRx() is called in a loop until the user of the software decides to stop (m_bBreak == true)

                 

                The class which handles the USB interaction and saves the sate is the following:

                 

                FX3VideoStreamer::FX3VideoStreamer(std::shared_ptr<spdlog::logger> Logger)
                : m_Logger(Logger), m_pUSBDevice(nullptr)
                {
                m_iXferCount = 0;
                m_lFbByteOffset = 0;
                m_bBreak = false;
                }
                
                
                FX3VideoStreamer::~FX3VideoStreamer() 
                {
                vAbortXferLoop();
                }
                
                
                /*****************************************************************
                *  Looking for FX3 device with VID and PID (SS mode) and get EP  *
                *****************************************************************/
                int FX3VideoStreamer::iOpenDevice(int i)
                {
                SAFE_DELETE(m_pUSBDevice);
                // Creating new pointer to USB device.
                if (!m_pUSBDevice)
                {
                GUID guid = { 0xEE9E7C37, 0x4714, 0x4B31, 0x8A, 0xE1, 0x41, 0xE1, 0xE7, 0x8B, 0xB0, 0xF9 };
                m_pUSBDevice = new CCyUSBDevice(NULL, guid);
                }
                // Opening USB device.
                for (int j = 0; j < m_pUSBDevice->DeviceCount(); ++j)
                {
                if (m_pUSBDevice->IsOpen() || m_pUSBDevice->Open(j))
                break;
                
                
                m_pUSBDevice->Close();
                }
                // Verifying the USB device connection.
                if (!m_pUSBDevice->IsOpen())
                {
                m_Logger->error("FX3VideoStreamer::iAppUsbInit: Couldn't open the Frammgrabber USB device");
                return APP_ERROR_DEVICE_NOT_FOUND;
                }
                
                // Verifying the VendorID and ProductID.
                if (m_pUSBDevice->VendorID != DEVICE_VID || m_pUSBDevice->ProductID != DEVICE_PID)
                {
                if (i == 0)
                m_Logger->error("FX3VideoStreamer::iAppUsbInit: Wrong VedorID or ProductID");
                m_pUSBDevice->Close();
                return APP_ERROR_DEVICE_NOT_FOUND;
                }
                if (!m_pUSBDevice->bSuperSpeed)
                {
                if (i == 0)
                m_Logger->error("FX3VideoStreamer::iAppUsbInit: Device not in SuperSpeed mode");
                m_pUSBDevice->Close();
                return APP_ERROR_DEVICE_NOT_IN_SS_MODE;
                }
                // Get a first bulk IN endpoint.
                m_pEndPt = m_pUSBDevice->BulkInEndPt;
                // Get buffer length and the endpoint's transfers size.
                m_lRequestedXferSize = m_pEndPt->MaxPktSize * PACKETS_PER_XFER;
                
                
                return APP_SUCCESS;
                }
                
                
                /*****************************************************************
                *      USB initializing and preparing buffers and queue them     *
                *****************************************************************/
                int FX3VideoStreamer::iAppUsbInit()
                {
                // Opening USB device.
                int iCounter = 0;
                while (iOpenDevice(iCounter++) != APP_SUCCESS && !m_bBreak)
                {
                Sleep(10);
                };
                
                
                // Verifying break signal.
                if (m_bBreak)
                return APP_ERROR_UNKNOWEN;
                
                
                m_iOverflowCounter = 0;
                
                
                m_vBuffers.resize(XFERS_TO_QUEUE);
                for (int i = 0; i < XFERS_TO_QUEUE; i++)
                {
                // Prepare buffers.
                m_vBuffers[i].reset(new UCHAR[m_lRequestedXferSize]);
                m_inOvLap[i].hEvent = CreateEvent(NULL, false, false, NULL);
                
                
                memset(m_vBuffers[i].get(), 0xEF, m_lRequestedXferSize);
                
                
                // First batch of transfer requests.
                iBeginDataXFer(i);
                }
                
                
                return APP_SUCCESS;
                }
                
                
                /*****************************************************************
                *                       Data streaming phase                     *
                *****************************************************************/
                bool FX3VideoStreamer::bAppUsbStreamRx(FramebufferConfig_t *m_fbConfig)
                {
                try
                {
                bool isFullFrame = false;
                
                
                // Preset readLength with requester lenght.
                long lLastXferSize = m_lRequestedXferSize;
                
                
                // Wait till the transfer completion.
                bWaitForTransferCompletion(m_iXferCount);
                
                
                // Read the transferred data from the device.
                bool bRetVal = m_pEndPt->FinishDataXfer(m_vBuffers[m_iXferCount].get(), lLastXferSize, &m_inOvLap[m_iXferCount], m_pContexts[m_iXferCount]);
                m_bIsFinishedXFer[m_iXferCount] = true;
                
                
                if (!bRetVal || m_iOverflowCounter > PACKETS_PER_XFER)
                {
                if (!m_bBreak)
                {
                m_Logger->error("FinishDataXfer returned false. LastError: {}", m_pEndPt->LastError);
                vAbortXferLoop();
                iAppUsbInit();
                }
                }
                else
                {
                // Test if framebuffer has enough space
                if (m_lFbByteOffset + lLastXferSize > ROWS*COLS * 3)
                {
                // Reset write pointer and toggle framebuffer.
                m_lFbByteOffset = 0;
                m_fbConfig->ping_pong = !m_fbConfig->ping_pong;
                m_Logger->debug("-> Framegrabber overflow \n");
                m_iOverflowCounter++;
                }
                else
                {
                // Write frame data to framebuffer.
                if (m_fbConfig->ping_pong)
                {
                m_fbConfig->pongMtx.lock();
                memcpy(m_fbConfig->pongMat->data + m_lFbByteOffset, m_vBuffers[m_iXferCount].get(), lLastXferSize);
                m_fbConfig->pongMtx.unlock();
                }
                else
                {
                m_fbConfig->pingMtx.lock();
                memcpy(m_fbConfig->pingMat->data + m_lFbByteOffset, m_vBuffers[m_iXferCount].get(), lLastXferSize);
                m_fbConfig->pingMtx.unlock();
                }
                
                
                // Test if last buffer was full
                if (lLastXferSize % m_pEndPt->MaxPktSize)
                {
                // Reset write pointer and toggle framebuffer.
                m_lFbByteOffset = 0;
                m_fbConfig->ping_pong = !m_fbConfig->ping_pong;
                isFullFrame = true;
                }
                else
                {
                // Shift write pointer.
                m_lFbByteOffset = m_lFbByteOffset + lLastXferSize;
                }
                }
                // Re-submit this queue element to keep the queue full.
                iBeginDataXFer(m_iXferCount);
                }
                m_iXferCount++;
                if (m_iXferCount == XFERS_TO_QUEUE)
                m_iXferCount = 0;
                
                
                return isFullFrame;
                }
                catch (...)
                {
                return false;
                }
                }
                
                
                void FX3VideoStreamer::vAbortXferLoop()
                {
                if (!m_vBuffers.empty())
                {
                m_pEndPt->Abort();
                for (int i = 0; i < XFERS_TO_QUEUE; i++)
                {
                if (!m_bIsFinishedXFer[i])
                {
                m_pEndPt->FinishDataXfer(m_vBuffers[i].get(), m_lRequestedXferSize, &m_inOvLap[i], m_pContexts[i]);
                m_bIsFinishedXFer[i] = true;
                }
                m_vBuffers[i] = nullptr;
                CloseHandle(m_inOvLap[i].hEvent);
                }
                }
                SAFE_DELETE(m_pUSBDevice);
                }
                
                
                bool FX3VideoStreamer::bWaitForTransferCompletion(int i)
                {
                bool bRet = false;
                while (!bRet)
                {
                Ret = m_pEndPt->WaitForXfer(&m_inOvLap[i], XFER_TIMEOUT);
                
                
                if (!bRet)
                {
                m_Logger->debug("FramegrabberUsbConf::vWaitForTransferCompletion: WaitForXfer returned false. LastError = {}, Count = {}, packet = {} \n", m_pEndPt->LastError, m_iXferCount, i);
                
                
                if (m_bBreak)
                bRet = true;
                }
                }
                return bRet;
                }
                
                
                int FX3VideoStreamer::iBeginDataXFer(int i)
                {
                int bRet = APP_ERROR_UNKNOWEN;
                while (bRet != APP_SUCCESS)
                {
                m_pContexts[i] = m_pEndPt->BeginDataXfer(m_vBuffers[i].get(), m_lRequestedXferSize, &m_inOvLap[i]);
                if (m_pEndPt->NtStatus || m_pEndPt->UsbdStatus)
                {
                if (m_bBreak)
                {
                bRet = APP_SUCCESS;
                }
                else
                {
                m_Logger->error("FramegrabberUsbConf::iBeginDataXFer: Xfer request rejected. NtStatus = {}, UsbdStatus = {}, Count = {}, packet = {} \n", m_pEndPt->NtStatus, m_pEndPt->UsbdStatus, m_iXferCount, i);
                bReconnectDevice();
                }
                }
                else
                {
                m_bIsFinishedXFer[i] = false;
                bRet = APP_SUCCESS;
                }
                }
                return bRet;
                }
                
                
                bool FX3VideoStreamer::bReconnectDevice()
                {
                if (m_pUSBDevice != NULL)
                return m_pUSBDevice->ReConnect();
                
                
                return 0;
                }
                
                
                /* ############################## Header ##################################### */
                
                
                
                
                #pragma once
                
                
                #include <opencv2/core.hpp>
                #include "AbstractModule.hpp"
                #include <opencv2/core.hpp>
                #include "AbstractModule.hpp"
                #include <atomic>
                #include <CyAPI.h>
                #include "util.h"
                
                
                #pragma warning (disable : 4996)
                #define XFERS_TO_QUEUE 64
                #define ROWS 720
                #define COLS 1920
                
                
                #include <atomic>
                
                
                struct FramebufferConfig_t
                {
                std::atomic<bool> ping_pong;
                std::mutex pingMtx;
                std::mutex pongMtx;
                
                
                std::shared_ptr<cv::Mat> pingMat;
                std::shared_ptr<cv::Mat> pongMat;
                };
                
                
                enum AppReturnCode
                {
                APP_SUCCESS,
                APP_ERROR_UNKNOWEN,
                APP_ERROR_DEVICE_NOT_FOUND,
                APP_ERROR_DEVICE_NOT_IN_SS_MODE,
                APP_ERROR_BEGIN_XFER_FAILED,
                APP_ERROR_XFER_TIMEOUT
                };
                
                
                class FX3VideoStreamer : public QObject
                {
                Q_OBJECT
                
                
                int m_iXferCount;
                long m_lFbByteOffset;
                
                
                long m_lRequestedXferSize;
                
                
                std::shared_ptr<spdlog::logger> m_Logger;
                
                
                CCyUSBDevice *m_pUSBDevice;
                CCyUSBEndPoint *m_pEndPt;
                std::vector<std::unique_ptr<UCHAR[]>> m_vBuffers;
                PUCHAR m_pContexts[XFERS_TO_QUEUE];
                OVERLAPPED m_inOvLap[XFERS_TO_QUEUE];
                bool m_bIsFinishedXFer[XFERS_TO_QUEUE];
                
                
                FramebufferConfig_t m_fbConfig;
                
                
                void vAbortXferLoop();
                bool bWaitForTransferCompletion(int i);
                int iBeginDataXFer(int i);
                int iOpenDevice(int i);
                bool m_bBreak;
                int m_iOverflowCounter; 
                
                
                public:
                explicit FX3VideoStreamer(std::shared_ptr<spdlog::logger> Logger);
                ~FX3VideoStreamer();
                int iAppUsbInit();
                
                
                bool bAppUsbStreamRx(FramebufferConfig_t *fbConfig);
                
                
                bool bAbortEndpoint();
                bool bResetEndpoint();
                bool bResetDevice();
                bool bReconnectDevice();
                bool bResetFpga();
                
                
                void SetBreak(bool bBreak)
                {
                m_bBreak = bBreak;
                }
                
                
                };