Hi All
In order to reach 192MBits/s on an isochronous IN endpipe I have a large buffer holding the data and expect that the HS controller sends this buffer autonomously - sending three 1024 byte packets in each microframe:
24 micro frames a ms = 24kBytes data per ms, = 24MBytes/s (192M Bits/s).
However it is not functioning autonomously and also doesn't send full multiple frames in a microframe and so I am wondering what causes this?
It should be straightforward by filling out the queue header as follows:
1. Maximum Packet Length is 1024 bytes (maximum allowed for HS isochronous)
2. The 'large' data buffer is pointed to by the Buffer pointers (each page aligned on a 4k boundary)
3. The Mult setting is set to 3 - send 3 frames per microframe interval.
4. Total Bytes is the size of the large buffer (eg. 16k)
Expected is that the HS USB device sends 3 x 1024 bytes per microframe and repeats until all 16k has been sent - then it retires.
What however happens is that it sends three transfers in the first microframe - the first of 1024 bytes (as expected) but the second with 128 bytes and a third for zero bytes (and stops sending anything else in following microframes). I have no explanation as to why it would do that and not send 3 x 1024?
If I just send one frame per microframe it sends just the single 1024 byte package and retires.
Therefore the other question is why it retires after one frame rather than continuing until the Total Bytes count has reached zero and sending data until the count has decremented to zero - potentially sending a smaller final frame if the buffer size is not divisible by 1024 (which is what is expected)?
Any ideas?
Regards
Mark
Hello Mark,
Does this error also occurred when using different MULT value? MULT=2 for example.
In addition, please see the below extract from the reference manual:
The transaction error bit set in the status field indicates a fulfillment error condition. When a fulfillment error occurs, the frame after the transfer failed to complete wholly, and the device controller retires the current ISO-dTD and move to the next ISO-dTD.
Could you please check if the transaction error bit is set when the package retires?
Best regards,
Felipe
Hi Felipe
Many thanks for the idea - in fact there was no transaction error set, and in addition I realised that the behavior of the HS device was to send the second DATA frame (DATA1) with a CRC as well as the unexpected lengths.
However I think that I could solve the issue by controlling the MultO (Multiplier Override) in the dTD token:
Originally I assumed that this didn't have to be set if the MULT in the header was controlling things but it turns out that the strange behavior takes place when this is left at 0. If I set this to 3 I get 3 x 1024 DATA frames being sent in the micro-frame.
So I also took the advice to optimised the override value according to the exact length and that allowed correct operation on all lengths. In the process I learned about DATA2, DATA1, DATA0 operation, which I hadn't experienced before (the HS USB design is very clever!)
ptrTxQueueHeader->dTD.ul_dtToken = ((usLen << ENDPOINT_QUEUE_HEADER_TOKEN_TOTAL_BYTES_SHIFT) | ENDPOINT_QUEUE_HEADER_TOKEN_IOC | ENDPOINT_QUEUE_HEADER_TOKEN_STATUS_ACTIVE); // the length of data to be sent with interrupt on completion
if ((ptrTxQueueHeader->ulCapabilities & ENDPOINT_QUEUE_HEADER_MULT_3) != 0) { // if high speed isochronous endpoint configured to send multiple frames each micro-frame
unsigned long ulMaximumPacketLength = ((ptrTxQueueHeader->ulCapabilities & ENDPOINT_QUEUE_HEADER_MAX_PACKET_LEN_MASK) >> 16);
unsigned long ulThisSize = usLen;
if ((ulThisSize <= ulMaximumPacketLength) || ((ptrTxQueueHeader->ulCapabilities & ENDPOINT_QUEUE_HEADER_MULT_3) == ENDPOINT_QUEUE_HEADER_MULT_1)) {
ptrTxQueueHeader->dTD.ul_dtToken |= ENDPOINT_QUEUE_HEADER_TOKEN_MULTO_1; // return single data frame in the isochronous micro-frame
}
else {
ulThisSize -= ulMaximumPacketLength;
if ((ulThisSize <= ulMaximumPacketLength) || ((ptrTxQueueHeader->ulCapabilities & ENDPOINT_QUEUE_HEADER_MULT_3) == ENDPOINT_QUEUE_HEADER_MULT_2)) {
ptrTxQueueHeader->dTD.ul_dtToken |= ENDPOINT_QUEUE_HEADER_TOKEN_MULTO_2; // return two data frames in the isochronous micro-frame
}
else {
ptrTxQueueHeader->dTD.ul_dtToken |= ENDPOINT_QUEUE_HEADER_TOKEN_MULTO_3; // return three data frames in the isochronous micro-frame
}
}
}
So for data lengths of 2049..3072 it does this
[full length DATA2 packet followed by full length DATA1 packet plus rest in DATA0 packet]
For data length of 1025..2048 it does
[full length DATA1 packet plus rest in DATA0 packet]
and 0..1024 it does
[single in DATA0 packet]
Therefore the operation is now good but the HS USB behavior when the MultO is left at 0 looks to be undefined and so must be avoided.
I was still thinking that I could queue more data content but that doesn't seem to be possible since it always retires after sending the 3 data packets when I have a data length set greater. This suggests to me that this is a limitation on isochronous endpoints (if I remember correctly bulk endpoints can send up to 20k of data with a single descriptor). I suppose that linked descriptors are needed if larger amounts of isochronous data need to be prepared and then sent autonomously, and seeing as 3072 bytes can be prepared for each isochronous IN this can still be done pretty efficiently.
Regards
Mark
Hello Mark,
I am glad to hear that you were able to solve the issue, and thank you for sharing the solution with the community!
Best regards,
Felipe