Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Cypress EZ-USB FX2 streaming problems with CyAPI and CyUSB

Status
Not open for further replies.

Elephantus

Junior Member level 3
Joined
Jul 11, 2005
Messages
31
Helped
4
Reputation
8
Reaction score
0
Trophy points
1,286
Activity points
1,627
cyusb

I am designing a streaming data acquisition application with the Cypress EZ-USB FX2, using CyAPI. The data is transferred from external logic via the slave FIFO interface to a quad-buffered bulk endpoint in the FX2. The PC fetches data from the FX2 using transfer prescheduling by queuing overlapped transfers (the BeginXfer-WaitXfer-FinishXfer approach as shown in the CyAPI streamer example).
The given approach should yield continuous fetching of data on the USB bus and extensive buffering is used on the PC in order to maximize stream throughput.

However,upon testing and measurements, monitoring the FLAGB (FIFO full flag) has shown that the FX2 fifo is being emptied non-continuously depending on the current CPU load. In an application which uses three parallel threads(one to fetch and pre-schedule USB data, second to receive data and trigger processing and third which simulates processing for a given duration) monitoring the FLAGB had shown that the FX2 FIFO was full(unserviced by the USB Host) for the exact duration of the simulated processing (a specified period of high CPU load). Similar results were achieved when the overall CPU load was raised using a parallel CPU-time consuming process.

Additional measurements and observation of the FLAGB behavior indicated that there is a possibility that the fetch and preschedule thread is not given sufficient CPU time, resulting in the unsufficient speed of transfer rescheduling, which empties the prescheduled transfer queue. However, increasing the buffer/transfer sizes and changing the thread priority did not eliminate the problem. For the given design, achieving the continuous data streaming is crucial due to limited buffering capabilities of the underlying hardware. The non-continuous fetching of data results in a data loss due to buffer overflow, which is unacceptable in the given design.

The questions are: is the dependency of the transfer rate on the CPU load caused by the CyAPI/CyUSB driver architecture, and is there a way to increase performance with the CyUSB driver? Could performance be increased by directly accessing the CyUSB driver via the CyIOCTL interface?

If anyone has an answer, or any other ideas about the given problem, I would appreciate any help.
 

cyapi

to fully use the bandwidth of cypress FX2, which is a USB2.0 device, the PC has a tremendous workload.

As we observed on previous projects, during the streaming process, any window-dragging will cause temporary packet stall.

It's sorta system limit. Not caused by driver or sth.
 

    Elephantus

    Points: 2
    Helpful Answer Positive Rating
begindataxfer

Well the bandwidth we were hoping to utilize is somewhat around 30-40 megabits per second, however any additional CPU load whatsoever seems to break that bandwidth.
Somehow it seems that the application, performing the given tasks, cannot sustain the needed throughput all the time or at all, regardless of the fact that the FX2 bandwidth is not fully utilized.
 

ioctl_adapt_send_non_ep0_direct

I know this is an old thread, but if I can find it, other will too.

I programmed data transfer from CCD-Sensor module with Cypress USB and switch from CyAPI to CyUSB because it seems that overlapped IO is not really working in CyAPI.

With direct access to CyUSB (via DeviceIOCtrl) I can start transfer into two separate buffers and the driver returns each buffer when it is filled. As soon as one buffer is filled, I start another buffer so that there are always 2 buffers receiving.

Theoretically you could do the same with CyAPI, but if I do so, I get the first callback when both buffers are finished.

So the only way for me is to make direct use of CyUSB.

1) Include "cyioctl.h"

2) Enumerate (sorry, German comments)
Code:
/// GUID des Cypress USB Treibers
static GUID CYUSBDRV_GUID = {0xae18aa60, 0x7f6a, 0x11d4, 0x97, 0xdd, 0x0, 0x1, 0x2, 0x29, 0xb9, 0x59}; 

/// Zeilenanfangskennung
static const unsigned int XXX_LINE_MAGICNUMBER = 0x555A;

/// Adresse des USB Endpoints
#define XXX_TX_ENDPOINT_ADDR		0x08

/// Transferdatenblockgröße
#define DEFAULT_TX_TRANSFER_SIZE	 64

	// Handle auf den Gerätetreiber holen
	HDEVINFO hwDeviceInfo = SetupDiGetClassDevs((LPGUID) &CYUSBDRV_GUID, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
	if (hwDeviceInfo != INVALID_HANDLE_VALUE)
	{ 
		// devInterfaceData Struktur vorbereiten
		SP_DEVICE_INTERFACE_DATA devInterfaceData;
		devInterfaceData.cbSize = sizeof(devInterfaceData);

		// Liste aller vorhandenen Module erstellen
		while( (modules.size() < XXX_MAX_MODULES) && (SetupDiEnumDeviceInterfaces(hwDeviceInfo, 0, (LPGUID) &CYUSBDRV_GUID, (DWORD)modules.size(), &devInterfaceData)))
		{
			// deviceInfoData Struktur vorbereiten
			SP_DEVINFO_DATA deviceInfoData;
			memset(&deviceInfoData, 0, sizeof(SP_DEVINFO_DATA));
			deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

			// deviceInterfaceDetailData Struktur vorbereiten
			PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData;
			ULONG requiredLength = 0, reservedLength = 0, realLength = 0;
			if (!SetupDiGetDeviceInterfaceDetail(hwDeviceInfo, &devInterfaceData, NULL, 0, &requiredLength, NULL))
			{
				int errorCode = GetLastError();
				if (errorCode != ERROR_INSUFFICIENT_BUFFER)
				{
					perror("ERROR: SetupDiGetDeviceInterfaceDetail()[1] failed - ");
					printf("ERROR CODE %d (0x%X)\n", errorCode, errorCode);
					FreeModuleList();
					return ERR_XXX_SETUPAPI;
				}
			}
			reservedLength = requiredLength;
			deviceInterfaceDetailData = (PSP_INTERFACE_DEVICE_DETAIL_DATA) new char[reservedLength];
			memset(deviceInterfaceDetailData, 0, reservedLength);
			deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

			// Details abfragen
			realLength = reservedLength;
			if (SetupDiGetDeviceInterfaceDetail(hwDeviceInfo, &devInterfaceData, deviceInterfaceDetailData, reservedLength, &realLength, &deviceInfoData))
			{ 
				// Modul initialisieren
				XXXModule* newModule = new XXXModule((int)modules.size(), deviceInterfaceDetailData->DevicePath);
				modules.push_back(newModule);
			} else
			{
				int error = ::GetLastError();
				std::cerr << "XXXEnumerator::UpdateModuleList: SetupDiGetDeviceInterfaceDetail failed with code " << error << " \"" << ErrorMessage(error) << "\"" << std::endl;
				FreeModuleList();
				return ERR_XXX_SETUPAPI;
			}

			// Für deviceInterfaceDetailData reservierten Speicher freigeben
			delete deviceInterfaceDetailData;
			deviceInterfaceDetailData = NULL;
		}

		// Auflistung beenden
		SetupDiDestroyDeviceInfoList(hwDeviceInfo);

		// Fertig
		return ERR_XXX_OK;

	// Gerätetreiber nicht vorhanden -> ergo keine Module
	} else
	{
		return ERR_XXX_NODRIVER;
	}


3) Open device
Code:
	// Gerät öffnen
	device = CreateFile(devicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	if (device == INVALID_HANDLE_VALUE) return false;

4) Define Transfer size
Code:
// Transfergröße für Empfang definieren
	SetTransferInfo.EndpointAddress = XXX_RX_ENDPOINT_ADDR;
	SetTransferInfo.TransferSize = transfer_size;
	if (!(result = DeviceIoControl(device, IOCTL_ADAPT_SET_TRANSFER_SIZE, &SetTransferInfo, sizeof(SET_TRANSFER_SIZE_INFO), &SetTransferInfo, sizeof(SET_TRANSFER_SIZE_INFO), &bytesTransferred, NULL)))
	{
		perror("ERROR: DeviceIoControl(IOCTL_ADAPT_SET_TRANSFER_SIZE) for RX failed in XXXModule::Open - ");
		return false;
	}

	// Transfergröße für Sendung definieren
	SetTransferInfo.EndpointAddress  = XXX_TX_ENDPOINT_ADDR;
	SetTransferInfo.TransferSize = transfer_size;
	if (!(result = DeviceIoControl(device, IOCTL_ADAPT_SET_TRANSFER_SIZE, &SetTransferInfo, sizeof(SET_TRANSFER_SIZE_INFO), &SetTransferInfo, sizeof(SET_TRANSFER_SIZE_INFO), &bytesTransferred, NULL)))
	{
		perror("ERROR: DeviceIoControl(IOCTL_ADAPT_SET_TRANSFER_SIZE) for TX failed in XXXModule::Open - ");
		return false;
	}

5) Transfer
Code:
DWORD dwReturnBytes;															  //Bulk Transfer
	PSINGLE_TRANSFER pTransfer;

	if (device == INVALID_HANDLE_VALUE ) return -1;

	ZeroMemory(pXmitBuf, sizeof(SINGLE_TRANSFER));
	pTransfer = (PSINGLE_TRANSFER) pXmitBuf;
	pTransfer->WaitForever = FALSE;
	pTransfer->ucEndpointAddress = Address;
	pTransfer->IsoPacketLength = 0;
	pTransfer->BufferOffset = 0;
	pTransfer->BufferLength = 0;

	BOOL result = DeviceIoControl(device, IOCTL_ADAPT_SEND_NON_EP0_DIRECT, pXmitBuf, sizeof(SINGLE_TRANSFER), buf, bufLen, &dwReturnBytes, ov);

	if (result == 0 && ov == NULL)
	{
		perror("XXXModule::directXfer()->DeviceIoControl(IOCTL_ADAPT_SEND_NON_EP0_DIRECT) failed for synchronous transfer - ");
		return false;
	}

Warning: If I set a transfersize >2MiB, I get the BSOD
 

finishdataxfer

Overlapped reads aren't very well documented, but they do work. You will never do better than about 15 MB/sec in my experience with the standard blocking read calls, and you'll get severe dropouts at any rate above ~6 MB/sec.

I've used this code to receive up to about 32 mbyte/sec from the Cypress FX2. It has run for days at a time with no dropouts at 10 mbyte/sec (with a 16KB hardware FIFO.)

Code:
S32 ADC_acquire(void)
{
   OVERLAPPED inOv [QSIZE];  // 32 in my case
   static U8 *buff [QSIZE];      
   U8    *contexts [QSIZE];

   BulkInPipe4->SetXferSize(NBYTES); // 65536 in my case

   ADC_enable_acquisition(1); // (application-specific)

   S32 max_size = BulkInPipe4->MaxPktSize;
   S32 xfer_size = BulkInPipe4->GetXferSize();

   printf("MaxPkt=%d, XferSize=0x%X, bHighSpeed=%d\n", 
      max_size, xfer_size, USBDevice->bHighSpeed );

   assert(NSAMPLES == (xfer_size/4));

   for (S32 i=0; i < QSIZE; i++)
      {
      buff[i] = (U8 *) malloc(xfer_size);
      assert(buff[i] != NULL);

      memset(&inOv[i], 0, sizeof(inOv[i]));
      inOv[i].hEvent = CreateEvent(NULL, false, false, NULL);

      contexts[i] = BulkInPipe4->BeginDataXfer(buff[i], xfer_size, &inOv[i]); 
      }

   printf("Receiving data\n\n");

   S64 start = mono_time_uS();
   S64 elapsed = 0;
   S64 bytes = 0;

   S32 bufnum=0;

   S32 result = 0;

   for (;;)
      {
      elapsed = mono_time_uS() - start;

      if (_kbhit())  
         {
         result = (_getch() != 27);
         break;
         }

      // Wait for this xfer to complete

      if (!BulkInPipe4->WaitForXfer(&inOv[bufnum], 1000))
         {
         ADC_enable_acquisition(0);
         BulkInPipe4->Abort();
         assert(0);
         WaitForSingleObject(inOv[bufnum].hEvent, INFINITE);
         }

      LONG rLen = xfer_size;

      if (!BulkInPipe4->FinishDataXfer(buff[bufnum], rLen, &inOv[bufnum], contexts[bufnum]))
         {
         ADC_enable_acquisition(0);
         show_last_error();
         exit(1);
         }

      // Process NBYTES of data from buff[bufnum]

      U8 *src = buff[bufnum];
      // ...

      // Resubmit the transfer

      contexts[bufnum] = BulkInPipe4->BeginDataXfer(buff[bufnum], xfer_size, &inOv[bufnum]);

      bufnum = (bufnum + 1) % QSIZE;
      bytes += rLen;
      }

   ADC_enable_acquisition(0);
   flush(0);

   if (elapsed)
      {
      printf("\n\n%I64d bytes, %I64d usec = %I64d bytes/sec",
         bytes,
         elapsed,
         bytes * 1000000 / elapsed);
      }

   // Cleanup

   BulkInPipe4->Abort();

   for (S32 i=0; i < QSIZE; i++)
      {
      if (!BulkInPipe4->WaitForXfer(&inOv[i], 1000))
         {
         BulkInPipe4->Abort();
         WaitForSingleObject(inOv[i].hEvent, INFINITE);
         }

      LONG rLen = xfer_size;
      BulkInPipe4->FinishDataXfer(buff[i], rLen, &inOv[i], contexts[i]);

      CloseHandle(inOv[i].hEvent);

      free(buff[i]);
      buff[i] = NULL;
      }

   return result;
}
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top