EmbeddedRelated.com
Forums

USB with LPC2148: When to fire a new data transfer from device to PC (bulk endpoint) ?

Started by Martin Maurer October 2, 2005
Hello,

i am using a LPC2148 via USB, connected to my PC (under WinXP and libusb-win32, PC has USB 2.0).
I am currently trying to understand how i can send data from LPC2148 to PC and increase my throughput.

I use the KEIL examples as base for my development (but currently instead of using an interrupt i just do polling and call the interrupt routine by myself, but this is a different thing...) and they generate a function call each time a IN Token is received. So it looks like this:

void USB_EndPoint5 (DWORD event)
{
switch (event)
{
case USB_EVT_IN:
{
NrOfIn++;
AllowedToWriteToEndpoint5 = TRUE;
// SendDataToEndpoint();
break;
}
}
}

I see that this routine is called around 975 times per second. I think this could be correct, because 1 ms looks reasonable (or should it be 8 times higher because of USB 2.0 ?). Ok, inside this routine i trigger a write (which is currently commented out) to endpoint, which sends 64 bytes. So with this implementation i should be able to send around 64 KByte/s. I think endpoint 5 is double buffered, so with another implementation i think i can send 2 times 64 kbytes/s, result around 128 kbytes/s (theoretically, not tried out). This is far away from 12 MBit/s (which should give around 1,5 MBytes/s, ok, can't reach the theoretical value).

Is this the right way to trigger a write ? Is there another possibility to determine if i can write new data into the endpoint's tramsit register ? Or are the 1 x 64 or 2 x 64 KByte/s the maximum i can get ? Someone already done this and can give me a hint ?

Regards,

Martin


An Engineer's Guide to the LPC2100 Series

Hi Martin,

> i am using a LPC2148 via USB, connected to my PC (under WinXP
> and libusb-win32, PC has USB 2.0).
> I am currently trying to understand how i can send data from
> LPC2148 to PC and increase my throughput.
>
> I use the KEIL examples as base for my development (but
> currently instead of using an interrupt i just do polling and
> call the interrupt routine by myself, but this is a different
> thing...) and they generate a function call each time a IN
> Token is received. So it looks like this:
>
> void USB_EndPoint5 (DWORD event)
> {
> switch (event)
> {
> case USB_EVT_IN:
> {
> NrOfIn++;
> AllowedToWriteToEndpoint5 = TRUE;
> // SendDataToEndpoint();
> break;
> }
> }
> }
>
> I see that this routine is called around 975 times per
> second. I think this could be correct, because 1 ms looks
> reasonable (or should it be 8 times higher because of USB 2.0
> ?).

This is a common misconception. USB 2.0 is a specification that
specifies a number of devices, notably full speed and high speed. USB
2.0 by itself does not mean high-speed 480Mbps. The 2148 is a USB 2.0
full speed device and therefore is limited to 12Mbps.

> Ok, inside this routine i trigger a write (which is
> currently commented out) to endpoint, which sends 64 bytes.

Is this a bulk endpoint (i.e. is it endpoint 5 IN, physical endpoint 11
in 2148 speak)?

> So with this implementation i should be able to send around
> 64 KByte/s. I think endpoint 5 is double buffered, so with
> another implementation i think i can send 2 times 64
> kbytes/s, result around 128 kbytes/s (theoretically, not
> tried out). This is far away from 12 MBit/s (which should
> give around 1,5 MBytes/s, ok, can't reach the theoretical value).

No. You should be able, easily, to achieve more than 64K per second if
you program things correctly.

> Is this the right way to trigger a write ?

You don't have to do it like that. That particular USB interrupt is
generated when the buffer is empty and can be written to--that's quite a
different matter from receiving an IN token addressed to that endpoint.

> Is there another
> possibility to determine if i can write new data into the
> endpoint's tramsit register ? Or are the 1 x 64 or 2 x 64
> KByte/s the maximum i can get ? Someone already done this and
> can give me a hint ?

Bulk transfer is not limited to 64K/second in or out. You are confusing
things here--bulk transfers are not guaranteed bandwidth, they use
whatever is left over after the host allocates bandwidth for other
endpoints.

Here's a trace I took of transactions between the host and a device when
reading a file from a USB CF adapter:

Container title<Read (Lba 72)> device<1> endpoint<2> status<OK>
speed<FS>
time<8.496 536 708>
Container title<Command Transport> device<1> endpoint<2> status<OK>
speed<FS> time<8.496 536 708>
Transaction type<OUT> device<1> endpoint<2> status<ACK>
speed<FS>
time<8.496 536 708>
Container title<Data Transport> device<1> endpoint<2> status<OK>
speed<FS>
time<8.498 535 916>
Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
time<8.498 535 916>
Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
time<8.498 590 416>
Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
time<8.498 645 333>
Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
time<8.498 699 500>
Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
time<8.498 754 250>
Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
time<8.498 808 583>
Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>

You will see that the IN transactions on endpoint 2 are not 1ms spaced.
Do not confuse frames (which start every ms) with transactions (which
occur within those frames).

--
Paul Curtis, Rowley Associates Ltd http://www.rowley.co.uk
CrossWorks for MSP430, ARM, AVR and now MAXQ processors



Hello Paul,

thanks for your clarifications ! But i still have no clue how to solve my problem:

First to answer your questions:
It is logical endpoint 5, physical endpoint 11, bulk, which i want to use with packet size of 64 bytes. Perhaps also with double buffering if needed.

In usbhw.c inside interrupt routine, there is the following code:

eisr = EP_INT_STAT; /* Endpoint Interrupt Status */

In this variable eisr, there are two field EP5RX and EP5TX. And when reading the datasheet, it looks to be the EP5TX, which i need to use to send new data from device to host over endpoint 5. In fact when i have understand the code correcly, the set bit EP5TX triggers a call to USB_P_EP[m](USB_EVT_IN) which is remapped to the function void USB_EndPoint5 (DWORD event) (located in usbuser.c) which i mentioned in my previous email (see below). But this function is only called around 975 times per second, which limits my throughput to 975 / 1 s * 64 bytes = 62400 bytes / s. Is it the right bit i am using or is there another way ? I think the triggering via "IN token" was a misunderstanding (because of switch-case USB_EVT_IN) ?

Could it be that my code inside PC is reading the data to slow ? Currently i am triggering a

length_status = usb_bulk_read(current_handle, 0x85, ReceiveBuffer, 64, 1000);

in a while loop (endless) inside the usb receiver thread. All this runs in user mode. Can i increase the 64 bytes to something higher even when LPC2148
only supports a max packet size of 64. So is there a need that these values match ?

How can i determine which side makes the problems in throughput ?

Regards,

Martin

----- Original Message -----
From: "Paul Curtis" <plc@plc@...>
To: <lpc2000@lpc2...>
Sent: Sunday, October 02, 2005 11:29 PM
Subject: RE: [lpc2000] USB with LPC2148: When to fire a new data transfer
from device to PC (bulk endpoint) ? > Hi Martin,
>
>> i am using a LPC2148 via USB, connected to my PC (under WinXP
>> and libusb-win32, PC has USB 2.0).
>> I am currently trying to understand how i can send data from
>> LPC2148 to PC and increase my throughput.
>>
>> I use the KEIL examples as base for my development (but
>> currently instead of using an interrupt i just do polling and
>> call the interrupt routine by myself, but this is a different
>> thing...) and they generate a function call each time a IN
>> Token is received. So it looks like this:
>>
>> void USB_EndPoint5 (DWORD event)
>> {
>> switch (event)
>> {
>> case USB_EVT_IN:
>> {
>> NrOfIn++;
>> AllowedToWriteToEndpoint5 = TRUE;
>> // SendDataToEndpoint();
>> break;
>> }
>> }
>> }
>>
>> I see that this routine is called around 975 times per
>> second. I think this could be correct, because 1 ms looks
>> reasonable (or should it be 8 times higher because of USB 2.0
>> ?).
>
> This is a common misconception. USB 2.0 is a specification that
> specifies a number of devices, notably full speed and high speed. USB
> 2.0 by itself does not mean high-speed 480Mbps. The 2148 is a USB 2.0
> full speed device and therefore is limited to 12Mbps.
>
>> Ok, inside this routine i trigger a write (which is
>> currently commented out) to endpoint, which sends 64 bytes.
>
> Is this a bulk endpoint (i.e. is it endpoint 5 IN, physical endpoint 11
> in 2148 speak)?
>
>> So with this implementation i should be able to send around
>> 64 KByte/s. I think endpoint 5 is double buffered, so with
>> another implementation i think i can send 2 times 64
>> kbytes/s, result around 128 kbytes/s (theoretically, not
>> tried out). This is far away from 12 MBit/s (which should
>> give around 1,5 MBytes/s, ok, can't reach the theoretical value).
>
> No. You should be able, easily, to achieve more than 64K per second if
> you program things correctly.
>
>> Is this the right way to trigger a write ?
>
> You don't have to do it like that. That particular USB interrupt is
> generated when the buffer is empty and can be written to--that's quite a
> different matter from receiving an IN token addressed to that endpoint.
>
>> Is there another
>> possibility to determine if i can write new data into the
>> endpoint's tramsit register ? Or are the 1 x 64 or 2 x 64
>> KByte/s the maximum i can get ? Someone already done this and
>> can give me a hint ?
>
> Bulk transfer is not limited to 64K/second in or out. You are confusing
> things here--bulk transfers are not guaranteed bandwidth, they use
> whatever is left over after the host allocates bandwidth for other
> endpoints.
>
> Here's a trace I took of transactions between the host and a device when
> reading a file from a USB CF adapter:
>
> Container title<Read (Lba 72)> device<1> endpoint<2> status<OK>
> speed<FS>
> time<8.496 536 708>
> Container title<Command Transport> device<1> endpoint<2> status<OK>
> speed<FS> time<8.496 536 708>
> Transaction type<OUT> device<1> endpoint<2> status<ACK>
> speed<FS>
> time<8.496 536 708>
> Container title<Data Transport> device<1> endpoint<2> status<OK>
> speed<FS>
> time<8.498 535 916>
> Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
> time<8.498 535 916>
> Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
> time<8.498 590 416>
> Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
> time<8.498 645 333>
> Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
> time<8.498 699 500>
> Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
> time<8.498 754 250>
> Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
> time<8.498 808 583>
> Transaction type<IN> device<1> endpoint<2> status<ACK> speed<FS>
>
> You will see that the IN transactions on endpoint 2 are not 1ms spaced.
> Do not confuse frames (which start every ms) with transactions (which
> occur within those frames).
>
> --
> Paul Curtis, Rowley Associates Ltd http://www.rowley.co.uk
> CrossWorks for MSP430, ARM, AVR and now MAXQ processors >
>
> Yahoo! Groups Links >
>




Martin,

> thanks for your clarifications ! But i still have no clue how
> to solve my problem:
>
> First to answer your questions:
> It is logical endpoint 5, physical endpoint 11, bulk, which i
> want to use with packet size of 64 bytes. Perhaps also with
> double buffering if needed.

Ok.

> In usbhw.c inside interrupt routine, there is the following code:
>
> eisr = EP_INT_STAT; /* Endpoint Interrupt
> Status */
>
> In this variable eisr, there are two field EP5RX and EP5TX.
> And when reading the datasheet, it looks to be the EP5TX,
> which i need to use to send new data from device to host over
> endpoint 5. In fact when i have understand the code correcly,
> the set bit EP5TX triggers a call to USB_P_EP[m](USB_EVT_IN)
> which is remapped to the function void USB_EndPoint5 (DWORD
> event) (located in usbuser.c) which i mentioned in my
> previous email (see below).

Ok.

> But this function is only called
> around 975 times per second, which limits my throughput to
> 975 / 1 s * 64 bytes = 62400 bytes / s. Is it the right bit i
> am using or is there another way ?

How heavily laden is your bus? The only way to know what's going on is
to get yourself a *hardware* USB analyzer. I've tried the software ones
and they are nowhere near as useful as the hardware one I have.

> I think the triggering via
> "IN token" was a misunderstanding (because of switch-case
> USB_EVT_IN) ?

USB_EVT_IN probably means "load some data ready to transfer to the
host", i.e. the IN buffer is empty and you need to load it so the next
IN token doesn't get a NAK response.

> Could it be that my code inside PC is reading the data to
> slow ? Currently i am triggering a
>
> length_status = usb_bulk_read(current_handle, 0x85,
> ReceiveBuffer, 64, 1000);
>
> in a while loop (endless) inside the usb receiver thread. All
> this runs in user mode. Can i increase the 64 bytes to
> something higher even when LPC2148 only supports a max packet
> size of 64. So is there a need that these values match ?

I can't tell you anything about the host end, I've no idea what
usb_bulk_read does for you or how it interacts with the OS stack.

> How can i determine which side makes the problems in throughput ?

Invest in a USB analyzer. There is no other way. You can see what's
happening on the bus and make a diagnosis from what you see.

Alternatively, invest in a USB stack that's already written?

--
Paul Curtis, Rowley Associates Ltd http://www.rowley.co.uk
CrossWorks for MSP430, ARM, AVR and now MAXQ processors