usb_cdc maximum bandwidth

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

usb_cdc maximum bandwidth

1,883 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by sebseb7 on Fri Sep 14 17:11:31 MST 2012
Hi,

is there a way to get more than 500000kbit/s with software usb_cdc (lpc1343) or rom usb_cdc (lpc1347) ?


a packet size seems to be maxium 64 bytes I find no wa increasing the bandwidth.


thank you
sebastian
0 Kudos
Reply
14 Replies

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by BrunoF on Wed Jul 09 21:02:48 MST 2014
Ok, i totally missed your previous post in this Topic talking about exactly that subject,

though i had to modified the code to:

void CDC_BulkIn(void) {
  int numBytesSend;
                                                     // split into packets
  numBytesSend = (cdc_bulkIN_count < USB_CDC_BUFSIZE) ? cdc_bulkIN_count : USB_CDC_BUFSIZE;

  if ( numBytesSend || ((numBytesSend == 0) && cdc_bulkIN_ZLP) ) {
    USB_WriteEP (CDC_DEP_IN, cdc_bulkIN_ptr, numBytesSend);  // send over USB
    cdc_bulkIN_count -= numBytesSend;
    cdc_bulkIN_ptr   += numBytesSend;
    cdc_bulkIN_ZLP   = (numBytesSend == USB_CDC_BUFSIZE);
    if ((!cdc_bulkIN_count) && (!cdc_bulkIN_ZLP))                            //<---added for mono-packet transfers to release flag
      cdc_bulkIN_occupied = FALSE;                                                 //<---added for mono-packet transfers to release flag
  } else {
    cdc_bulkIN_occupied = FALSE;
  }
}


Because flag cdc_bulkIN_occupied wasn't being released when i sent a packet of size < USB_CDC_BUFSIZE bytes. I know that if it's < USB_CDC_BUFSIZE bytes, then packet isn't a multi-packet really, but as only one callback is called and used to send data IN (CDC_BulkIn) i thought it was important to be able to just use the callback function for all kind of packets.

Cheers and thank you so much again,

B
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by BrunoF on Mon Jul 07 12:50:15 MST 2014
As always, you are right. You're a lifesaver.

I was not thinking about shared access to those things.

I do have a remaining question,

let's say i have to send 600 bytes to Host (IN endpoint). What would be the best way to achieve this transfer as faster as possible and without blocking the CPU?

Thanks a lot!!
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Tsuneo on Mon Jul 07 06:43:47 MST 2014
In your code, USB_ReadEP() is called at interrupt context, and USB_WriteEP() is called by the main loop. And then, violation of exclusive access to the shared resources (USB Endpoint control register, USB protocol engine) occurs.

Both of USB_ReadEP() and USB_WriteEP() touch to USBCtrl (LPC_USB->Ctrl) register, to transfer data over USBTxData / USBRxData registers. While these routines reads from / writes to the Tx/Rx registers, USBCtrl should be kept untouched. Similar problem occurs in the access to the USB protocol engine over USBCmdCode (LPC_USB->CmdData) and USBCmdData (LPC_USB->CmdData) registers.

There are a couple of solutions for this problem.
a) Mask USB (or endpoint) interrupt around USB_WriteEP() call in the main loop.
b) Move USB_WriteEP() into the endpoint ISR, and invoke the endpoint interrupt manually.
c) Register USB_ReadEP() and USB_WriteEP() as SVC routines.

Here is an example of b) option.

1) In VCOM_Serial2Usb() routine, USB_WriteEP() call is replaced with this line
   CDC_DepInEmpty = 0;
// USB_WriteEP (CDC_DEP_IN, (unsigned char *)&serBuf[0], 2);  // comment this line
   LPC_USB->DevIntSet = 1 << (EPAdr( CDC_DEP_IN ) + 1);   // invoke endpoint interrupt


2) in the USB_EndPoint1() callback, USB_WriteEP() is called.
As this callback is also called when the bulk IN transaction completes, CDC_DepInEmpty is applied to distinguish the first manual call and the secondary complete call.
void USB_EndPoint1 (uint32_t event) {
   if ( (event == USB_EVT_IN) && (CDC_DepInEmpty == 0) ) {
      CDC_DepInEmpty = 1;
      USB_WriteEP (CDC_DEP_IN, (unsigned char *)&serBuf[0], 2);
   }
}


Tsuneo
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by BrunoF on Sun Jul 06 11:22:30 MST 2014
Thanks a lot for your reply Tsuneo!

I'm using LPCXPresso 5 IDE and the base example is the usbcdc example inside file ...\lpcxpresso\Examples\NXP\LPC1000\LPC13xx\examples.lpc13xx.new.zip.

I've modified it to just keep the bulk part.

in usbhw.c, function void USB_IRQHandler (void) is the function called when a USB IRQ interrupt occur.

          if (USB_P_EP[m]) {
            USB_P_EP[m](USB_EVT_OUT);
          }


is the code part that executes

void USB_EndPoint2 (uint32_t event) {
      CDC_BulkOut ();                /* data received from Host */
}


void CDC_BulkOut(void) {
  static uint32_t led_state = 0;

  led_state++;

if(led_state & 0x80)
  GPIOSetValue( LED_PORT, LED_BIT, LED_ON );
else
  GPIOSetValue( LED_PORT, LED_BIT, LED_OFF );

  // get data from USB into intermediate buffer
  numBytesRead = USB_ReadEP(CDC_DEP_OUT, &BulkBufOut[0]);

  // ... add code to check for overwrite

  // store data in a buffer to transmit it over serial interface
  //CDC_WrOutBuf ((char *)&BulkBufOut[0], &numBytesRead);
}


That's all code respecting to OUT transfers. I've disable all my other code that made something usefull with the incoming data packets.

my main code is:

int main (void) {
  /* Basic chip initialization is taken care of in SystemInit() called
   * from the startup code. SystemInit() and chip settings are defined
   * in the CMSIS system_<part family>.c file.
   */

/* Enable Timer32_1, IOCON, and USB blocks */
LPC_SYSCON->SYSAHBCLKCTRL |= (EN_TIMER32_1 | EN_IOCON | EN_USBREG);

  /* Initialize 32-bit timer 0. TIME_INTERVAL is defined as 10mS */
  /* You may also want to use the Cortex SysTick timer to do this */
  /* interrupt each 16 mS **/
  init_timer32(0, (SystemFrequency/LPC_SYSCON->SYSAHBCLKDIV)/62);
  /* Enable timer 0. Our interrupt handler will begin incrementing
   * the TimeTick global each time timer 0 matches and resets.
   */
  enable_timer32(0);

  /* Initialize GPIO (sets up clock) */
  GPIOInit();
  /* Set LED port pin to output */
  GPIOSetDir( LED_PORT, LED_BIT, 1 );
  /* LED off */
  GPIOSetValue( LED_PORT, LED_BIT, LED_OFF );


  USBIOClkConfig();

  VCOM_Init();                              // VCOM Initialization

  USB_Init();                               // USB Initialization
  USB_Connect(TRUE);                        // USB Connect

  NVIC_SetPriority(USB_IRQn, 0);//maximum interrupt prior for USB IRQ
  NVIC_SetPriority(TIMER_32_0_IRQn, 31);//minimum interrupt prior for TIMER32_0 IRQ

  while (!USB_Configuration) ;              // wait until USB is configured

  while (1) {                               // Loop forever
    VCOM_Serial2Usb();                      // read serial port and initiate USB event
    //VCOM_CheckSerialState();
//VCOM_Usb2Serial();
  } // end while
} // end main ()


void VCOM_Serial2Usb(void) {
  static char serBuf [USB_CDC_BUFSIZE];
         int  numBytesRead, numAvailByte;

          /* timer32_0_counter is incremented in TMR32_0 interrupt */
  if((timer32_0_counter) /*&& CDC_DepInEmpty*/)
  {
  timer32_0_counter = 0;
  serBuf[0] = 0x31;
  serBuf[1] = 0x60;

      CDC_DepInEmpty = 0;
  USB_WriteEP (CDC_DEP_IN, (unsigned char *)&serBuf[0], 2);
  }
}

Thing is, as you said, firmware stop reading the Endpoint 0x02, but that's because IRQ for OUT Transfers at 0x02 seems to stop happening. CDC_BulkOut() stops being called, so the LED stops blinking too. That's my visual warning that something went wrong. I can then see the USB Analyzer showing those kind of log like the one i've attached here.

I'm having troubles debugging the code too. I can't even use printf() to the IDE Console because it result in USB transfers failing.

Some tests i've made:

1) See if somehow USB_Configure() function was deconfiguring the Endpoint 0x02. It doesn't.
2) Speed up USB_IRQHandler(), skipping checking endpoints my app doesn't use. Even looping LPC_USB->DevIntSt state to see if other transaction (one of the 19) happened, so i can just read data without having to re-enter interrupt service routine. No change.

Thanks!
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Tsuneo on Sun Jul 06 08:31:27 MST 2014
Hi BrunoF,

In your USB log,
The bulk OUT transfers (C:I:E=01:00:02) run within 2ms (Duration) at the start up.
But at the sequence #2125, the bulk OUT transfer is canceled by the host app. because of timeout.
No USB error is reported between the last successful transfer (#2111) and canceled one (#2125)

Quote:

    Seq           Duration                                                  C:I:E
URB 0004-0003 ... 897 us   Bulk or Interrupt Transfer 4 bytes   buffer  out 01:00:02  Success (Success)
URB 0006-0005 ... 1.990 ms Bulk or Interrupt Transfer 511 bytes buffer  out 01:00:02  Success (Success)
...
...
URB 2111-2110 ... 1.987 ms Bulk or Interrupt Transfer 511 bytes buffer  out 01:00:02  Success (Success)
URB 2125-2112 ... 101.973 ms Bulk or Interrupt Transfer                 out 01:00:02  Cancelled (Canceled)


It suggests that the bulk OUT endpoint is not read out by your firmware any more after #2111.

If you would show us your code around the bulk OUT EP, more suggestion should come.
What is the base example of your code?

Tsuneo
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by BrunoF on Thu Jul 03 11:11:30 MST 2014
Hi Tsuneo,

i've read you in several forums and i know you're a Guru in the USB subject.

Maybe you can give me a hint with something because i'm out of ideas. I'm working with a LPC1343 Bulk USB project. I'm using endpoint 0x81 for IN transfers and 0x02 for OUT transfers (i would love to use endpoint 0x03 because of FIQ and double buffering, but im limited to 0x02 cause of Host Driver side).
Problem is that Host may send data fast (up to 1mBps). So, i must receive the data as faster as i can. I started modifying the CDC example for my purpose. Everything works fine. Host side sends around 520 byte each 30mS in my tests. It works just fine for sometimes a few seconds, sometimes something more (a minute or so). Nothing is dropped. Thing is that at a certain point, OUT transactions start to get Cancelled forever.
My main code doesn't make anything more than sending 2 bytes (IN) each 16mS intervals. I removed all code so i can just read the Endpoint 0x02 as fast as i can. Still, you can see in the Report i've attached to this post that starting from Seq 2112 Endpoint 0x02 seems to stop responding and i have no clue why or how to recover it from that state.

I would love to fix this problem, so ideally it coud not drop even one packet, but, if this happen a couple of times per minute, i can live with that too. But at least i need to know a way to:
1) knowing if there's a error with OUT transfers (when endpoint stop responding, the associated interrupt stop occurring too), so i don't know how to detect the error, specially if no interrupt occur to indicate the error;
2) recover from that permanent CANCELLED state somehow, so i can continue receiving future transfers.

Thanks in advance!
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Tsuneo on Mon Sep 17 07:10:32 MST 2012
You are mixing up [B]Transfer[/B] and [B]Transaction[/B], both are USB-specific terminology.
Read this explanation, as I suggested in my first post.

Transfer - transaction - packet
http://www.cygnal.org/ubb/Forum9/HTML/001627.html

Tsuneo
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by sebseb7 on Mon Sep 17 05:10:56 MST 2012

Quote: Tsuneo
Does USB_FS_MAX_BULK_PACKET give wMaxPacketSize of the bulk endpoints?
Then, you should not increase it. For full-Speed bulk, 64 bytes is the greatest value.
Rather, increase the transfer size.



if 64 bytes is the maximum that can be transferred in one transfer, how one should reach more than 62.5kbyte/s ? As I understood only 1 transfer per millisecond can be done.

/sebastian
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Tsuneo on Mon Sep 17 04:29:30 MST 2012

Quote:
increasing USB_FS_MAX_BULK_PACKET in https://github.com/sebseb7/pentarm13...app_usbd_cfg.h does not help.


Does USB_FS_MAX_BULK_PACKET give wMaxPacketSize of the bulk endpoints?
Then, you should not increase it. For full-Speed bulk, 64 bytes is the greatest value.
Rather, increase the transfer size.

ErrorCode_t VCOM_bulk_out_hdlr(USBD_HANDLE_T hUsb, void* data, uint32_t event) 
{
    ...
    pVcom->rxlen = pUsbApi->hw->ReadEP(hUsb, USB_CDC_EP_BULK_OUT, pVcom->rxBuf);
    pVcom->rxBuf[pVcom->rxlen++] = pVcom->rxlen;               // <------ (1) delete this line
    pUsbApi->hw->WriteEP (pVcom->hUsb, USB_CDC_EP_BULK_IN, pVcom->rxBuf, pVcom->rxlen);     


The problem of above code lies in (1) line.

When your PC application sends more than 64 bytes to the COM port in single call, this transfer is split into multiple transactions by PC host controller. For example of 200 bytes transfer, four transactions, 64, 64, 64, 8, occur on the bulk OUT endpoint.
When every OUT transaction finishes, above VCOM_bulk_out_hdlr() is called over interrupt. In your code, ReadEP() retrieves a packet from the bulk OUT EP, and WriteEP() sends it back. The problem lies in above (1) line, at "pVcom->rxlen++". When 64 bytes transaction comes, this line increases pVcom->rxlen to 65. And then, this value is fed to WriteEP(). But wMaxPacketSize of bulk endpoints is 64, 65 is invalid. Delete (1) line.

Another problem of this code is, it doesn't terminate the bulk IN transfer, when the transfer size is multiple of 64 (64, 128, 192, ...). Your PC application doesn't see this size of transfer on the loopback, until it sends short transfer (less than 64 bytes) after it.

Tsuneo
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by sebseb7 on Sun Sep 16 22:55:43 MST 2012
increasing USB_FS_MAX_BULK_PACKET in https://github.com/sebseb7/pentarm13uxx/blob/usb_cdc/usb/app_usbd_cfg.h does not help.
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by sebseb7 on Sun Sep 16 22:54:18 MST 2012

Quote: Tsuneo
Heh, obvious?
I thought you meant high-speed (480M bps) USB
Anyway, bits/s is wrong unit to measure USB transfer speed, because bus bit rate is fixed.
Apply bytes/s, or full-size transactions (packets)/frame, instead.



sorry.



Quote: Tsuneo

Do you understand the code exactly?



not really. And I can't follow you (but thank you anyway) , because I use the lpc1347 rom driver.

You can find the code here: https://github.com/sebseb7/pentarm13uxx/blob/usb_cdc/main.c

this is only a test, which echoes everyting from usb back:

pVcom->rxlen = pUsbApi->hw->ReadEP(hUsb, USB_CDC_EP_BULK_OUT, pVcom->rxBuf);
pVcom->rxBuf[pVcom->rxlen++] = pVcom->rxlen;
pUsbApi->hw->WriteEP (pVcom->hUsb, USB_CDC_EP_BULK_IN, pVcom->rxBuf, pVcom->rxlen);     



thank you
sebastian
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Tsuneo on Sun Sep 16 22:04:14 MST 2012

Quote:
obviously I meant 500kbit/s ( 64byte per frame ; one frame per ms)


Heh, obvious?
I thought you meant high-speed (480M bps) USB
Anyway, bits/s is wrong unit to measure USB transfer speed, because bus bit rate is fixed.
Apply bytes/s, or full-size transactions (packets)/frame, instead.


Quote:
the hw_usb_cdc sample code for the lpc1347 seems to send only one packet per USB frame.



Do you understand the code exactly?
Where does "one packet per USB frame" come from?

As of usbcdc example in examples.lpc13xx.new.zip,
1) Host --> device direction
Whenever an OUT transaction finishes on the bulk OUT Endpoint, CDC_WrOutBuf() appends the packet to CDC_OutBuf.data[] array.
USB_EndPoint3(USB_EVT_OUT) --> CDC_BulkOut() --> CDC_WrOutBuf()

The firmware runs a cyclic buffer on this array. But the array size, CDC_BUF_SIZE, is set to 64 bytes.
cdcuser.c

#define CDC_BUF_SIZE   (64)     // Output buffer in bytes (power 2)

When transactions come from host too quickly, UART can't consume it in time, and then overwrite occurs on the cyclic buffer. To hold entire packets on the cyclic buffer, increase CDC_BUF_SIZE value.

For example, set it to 1024 bytes.
#define CDC_BUF_SIZE   (1024)     // Output buffer in bytes (power 2)

On your PC application, send 1024 bytes data to the COM port in single call.
PC host controller splits this transfer into 16 transactions of 64 bytes. Most likely these 16 transactions are sent to the device in single frame.

2) Device --> host direction
When the transfer size is greater than 64 bytes, you'll see multiple transactions in a frame.
The original code doesn't fit to large transfer size.
- It doesn't limit the packet size within 64 bytes when it calls USB_WriteEP() in CDC_BulkIn() and VCOM_Serial2Usb().
- It doesn't care transfer termination by ZLP, when the transfer size is just the multiple of 64.
In these reasons, the original code should be replaced to support large transfer size.

CDC_BulkIn() is replaced as follows.
cdcuser.c

uint32_t cdc_bulkIN_count    = 0;
uint8_t *cdc_bulkIN_ptr      = NULL;
uint8_t  cdc_bulkIN_occupied = FALSE;
uint8_t  cdc_bulkIN_ZLP      = FALSE;

void CDC_BulkIn(void) {
  int numBytesSend;
                                                     // split into packets
  numBytesSend = (cdc_bulkIN_count < USB_CDC_BUFSIZE) ? cdc_bulkIN_count : USB_CDC_BUFSIZE;

  if ( numBytesSend || ((numBytesSend == 0) && cdc_bulkIN_ZLP) ) {
    USB_WriteEP (CDC_DEP_IN, cdc_bulkIN_ptr, numBytesSend);  // send over USB
    cdc_bulkIN_count -= numBytesSend;           
    cdc_bulkIN_ptr   += numBytesSend;
    cdc_bulkIN_ZLP   = (numBytesSend == USB_CDC_BUFSIZE);
  } else {
    cdc_bulkIN_occupied = FALSE;
  }
}


To start large size transfer, fill a buffer with data, and pass the buffer to CDC_block_send()
uint32_t CDC_block_send( uint8_t *buffer, uint32_t send_size )
{
  if ( !cdc_bulkIN_occupied ) {                 // The bulk IN endpoint is not busy
    cdc_bulkIN_occupied = TRUE;                 // fill the context
    cdc_bulkIN_ptr   = buffer;
    cdc_bulkIN_count = send_size;
    cdc_bulkIN_ZLP   = FALSE;
    LPC_USB->DevIntSet = EPAdr( CDC_DEP_IN );   // trigger interrupt manually
    return TRUE;
  }
  return FALSE;
}


lastly, the context is initialized at Set_Configuration, to cope with detach/attach
usbuser.c

#if USB_CONFIGURE_EVENT
void USB_Configure_Event (void) {

  if (USB_Configuration) {                  /* Check if USB is configured */
    /* add your code here */
    cdc_bulkIN_count    = 0;
    cdc_bulkIN_ptr      = NULL;
    cdc_bulkIN_occupied = FALSE;
    cdc_bulkIN_ZLP      = FALSE;
  }
}
#endif


Tsuneo
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by sebseb7 on Sun Sep 16 10:31:38 MST 2012

Quote: Tsuneo
You would be better to learn the basic of USB, first.


LPC1347 has Full-Speed (FS) USB device engine. "Full-speed" means bus bit rate of 12M bps.
How can you carry 500000kbit/s over 12M bps?




obviously I meant 500kbit/s  ( 64byte per frame ; one frame per ms)



Quote: Tsuneo

FS Bulk transfer may have 19 full-size (64bytes) transactions at most, in single USB frame.
I've explained on Transfer - transaction on this post.



the hw_usb_cdc sample code for the lpc1347 seems to send only one packet per USB frame.
Does anyone know how to send multiple packets in one frame ?
0 Kudos
Reply

1,522 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Tsuneo on Sat Sep 15 20:37:45 MST 2012
You would be better to learn the basic of USB, first.


Quote:
is there a way to get more than 500000kbit/s with software usb_cdc (lpc1343) or rom usb_cdc (lpc1347) ?


LPC1347 has Full-Speed (FS) USB device engine. "Full-speed" means bus bit rate of 12M bps.
How can you carry 500000kbit/s over 12M bps?


Quote:
a packet size seems to be maxium 64 bytes I find no wa increasing the bandwidth.


FS Bulk transfer may have 19 full-size (64bytes) transactions at most, in single USB frame.
I've explained on Transfer - transaction on this post.

Transfer - transaction - packet
http://www.cygnal.org/ubb/Forum9/HTML/001627.html

Tsuneo
0 Kudos
Reply