08-28-2014 01:32 AM
I think I've found a bug in the usbps component of the Xilinx library and was hoping somebody can confirm this.
I have a revD ZedBoard, Vivado v2014.2 and a WinXP host PC.
I've been trying to develop a USB-CDC device end driver on the ZedBoard. Currently I'm still working on the control endpoint 0 - haven't got to the other endpoints yet.
I have no problems receiving the setup command and descriptor requests from the host over the OUT endpoint, but haven't been able to get the IN endpoint working reliably to transmit responses to the requests. I often get error 1412 returned from the EpBufferSend... calls, which is means that there are no transfer descriptors available (XST_USB_NO_DESC_AVAILABLE) when there are actually plenty available.
On further investigation, this is returned from XUsbPs_EnQueueRequest() when XUsbPs_dTDIsActive() returns true for the first transfer descriptor it checks, indicating that the TD is "active". The requirement is that available TDs should be marked inactive for available IN (transmit) descriptors.
The problem appears to be that when configuring the device with XUsbPs_ConfigureDevice(), the XUsbPs_dTDInit() function is called to initialise the TDs, which correctly initialises OUT TDs, but neglects to initialise the "active" bits of the IN TDs into the correct (inactive) state.
This would be a glaring bug, which would show that nobody actually seems to use the Zynq USB ports. The paucity of USB related forum topics appears to confirm this.
08-29-2014 05:42 PM
I would not say nobody uses the Zynq USB ports. I would say nobbody uses the USB ports using bare metal code. I think most people use the USB driver in u-boot or linux.
That said, I took a quick look at the bare metal code. I think the driver assumes that the TD memory space is initialized to zero before first use. That would make the TD flag set to 0 or inactive.
MemPtr = (u8 *)&Buffer;
DeviceConfig.DMAMemPhys = (u32) MemPtr;
Status = XUsbPs_ConfigureDevice(UsbInstancePtr, &DeviceConfig);
08-31-2014 06:54 PM
Thanks Norman. I agree it assumes the memory has been zeroed, but that confirms to me it is a bug, for 2 reasons:
1. I was seeing the problem even though I did initialise the memeory to zero. I traced it to the cache invalidation/flushing in XUsbPs_dTDInit(). I think that the zeroing of memory was still in cache and not flushed when it got trashed by the invalidation. This was only fixed when I did the zeroing in chunks in a loop and then did cache flushed immediately. There's no warning about this anywhere.
2. When there are alternate configurations it is possible for the host to request an alternate configuration, leading to calling XUsbPs_ReconfigureEp(), which in turn calls XUsbPs_dQHReinitEp() and XUsbPs_dTDReinitEp(). These behave the same as the ...Init() functions, except they will no longer be acting on zeroed memory.
Do you disagree?
08-31-2014 09:46 PM
I don't think I know enough to provide an informed opinion. I always though cache flash was included with cache invalidate. Guess not. It sounds like you have found some oversights in the code. Hopefully some Xilinx guys will comment.
01-05-2015 05:49 AM
I am also facing similar situation. The EP1 end point receives data from host. When I try to read from host, the IN end point does not respond? Did you solve it?
01-05-2015 04:47 PM
I reported the bug to Xilinx (I've attached the emails) along with suggested fixes. I have received no response. They don't appear to have any interest in getting bug reports, let alone fixing them.
The work-around I used is what I suggested in point 1 od my second message here: manually zero the memory, then manually flush the cache for that memory before (re)initializing.
02-05-2016 07:35 PM
Thanks for your post. I am also having similar troubles when attempting to BULK_IN data at high rates.
As I increase the throughput above 5MBytes/second, parts of my data stream vanish. Pushing it a little harder yields XST_USB_NO_DESC_AVAILABLE which seems non-recoverable. That errors persists no matter how many retries of XUsbPs_EpBufferSend().
After poking around I too hard concerns about cache coherency. Also variables being shared inside/outside of the IRQ context without the volatile attribute - seemed kind of wierd.
Steve - you last posted about this about a month ago. Any further insights?
02-07-2016 05:05 PM
I posted last about 13 months ago and don't remember much about it, except that both the problem and a suggested solution are in the emails I attached. I basically manually zeroed the memory and manually flushed the cache before calling the init functions.
07-19-2016 03:17 AM
I am planning to use the same driver. Where you able to get the bare-metal driver to work correctly?
And if so, did you use the solution swawryk was suggesting, or did you need to fix something else as well?
07-20-2016 04:56 PM
Just an update about how this all turned out for me.
The usbps code inside Vivado 2015.4 already includes the zero'ing of the memory buffer
that is passed in DeviceConfig.DMAMemPhys thru the XUsbPs_ConfigureDevice() call.
Cache coherence seemed ok for Zynq device to PC host but I never did derive anything
sensible from the USB DMA queueing code.
The USB feature is actually fairly stable now that I've fixed my code. (Sorry to Xilinx for my complaint.)
The big trouble we were having is, we missed the documentation warning with EpBufferSend().
You can not re-use the memory buffer submitted to EpBufferSend().
EpBufferSend() submits memory pointers of the submitted buffer to the USB DMA engine.
The upshot is if you re-use the buffer (say with the next chunk of user data) before
the DMA engine has finished its transfer, the data stream will be corrupted.
As a crude illustration...
- "ABCD" into EpBufferSend()
- DMA starts in the background...
- DMA manages to send "AB" just before your code re-uses the data buffer with "EFGH"..
- "EFGH" into EpBufferSend()
What can go out of the USB port is "ABGH" "EFGH".
Our project was using only the on-chip BRAM's so we were very memory constrained.
Hence, the odd desire to re-use memory buffers as early as possible.
I end-ed up hacking EpBufferSend() to block on 'DMA not complete' when I set a flag.
Most sane people won't need this feature.
Something else to watch out for was misaligned long word accesses to the BRAM controller
which resulted in DMA bus faults. The Xilinx bare metal USB sample code works when the
data is sourced from buffers located in DRAM (which uses the ARM block DDR memory controller).
That controller seems ok with mis-aligned long word fetches.
Happy USB'ing! ...and of course take all of this with 'a grain of salt'. Nobody's perfect.
04-01-2019 08:42 PM
I encounter the similar problem as you had.
My application is described as following:
PC software is host, in normal mode it sends read/write command to device (motor drives), and drive replies with data in 1 string. As for in graph mode the PC software sends read command to drive and drive needs to reply with data in 64 consecutive string. And my problem happens at replying 64 consecutive string in graph mode.
I configure the endpoint2 for IN (transmit) and endpoint3 for OUT (receive). I register xusb_cdc_ep2_irq_handler() as callback function for endpoint 2, but actually do nothing in this callback function even though I add the case XUSBPS_EP_EVENT_DATA_TX in xusb_cdc_ep2_irq_handler().
I set breakpoints after calling XUsbPs_EpBufferSend(), get the function's return value abnormal with XST_USB_NO_DESC_AVAILABLE code. I tried what you suggest to use different buffer to store data and then send the stored data into EpBufferSend(), but XST_USB_NO_DESC_AVAILABLE keeps happening. I also try to poll DMA hardware bit (XDMAPS_DBGSTATUS_OFFSET bit 0, is that right??) to check if DMA is busy or not, but It appears to be always busy even though I don't start the USB connection between PC software and drive.
Could you help me on this problem? Thanks in advance.