Forum Discussion
USB Device Issue on Cyclone V / HPS
- 26 days ago
@FabricNS
Please ignore above issue that wasn't it.
I played some more with the tinyusb code and finally found the problem. It seems the old dwc2 USB controller receive fifo size must be set to a minimum of 119 words. The fix is to edit the tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c file at the function calc_device_grxfsiz().
The original code is:
TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { return (uint16_t)(13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count); }I changed it to this:
TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { // Altera Cyclone V SoC DWC2 bug, the minimum grzfsiz is 119 words, i.e. 4*119 = 476 bytes #if defined(TUP_USBIP_DWC2_CV) && TU_CHECK_MCU(OPT_MCU_CV) uint16_t words = (uint16_t)(13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count); if(words < 119) words = 119; return words; #else return (uint16_t)(13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count); #endif }You will also need to add OPT_MCU_CV into tinyusb/src/common/tusb_mcu.h:
...... //--------------------------------------------------------------------+ // Geehy //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_APM32F0XX) #define TUP_USBIP_FSDEV #define TUP_USBIP_FSDEV_APM32 #define CFG_TUSB_FSDEV_PMA_SIZE 1024u //--------------------------------------------------------------------+ // Altera Cyclone V SoC //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_CV) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_CV #define TUP_DCD_ENDPOINT_MAX 16 #endifand also ensure to set it in your tusb_config.h:
#ifndef CFG_TUSB_MCU #define CFG_TUSB_MCU OPT_MCU_CV #endif
The traffic capture file
In HWLIB, you will need to correct the file:
include\soc_cv_av\socal\alt_usb.h
It contains registers with incorrect offset. Search for OFST. The problem starts from this code, so all registers after this:
/* The byte offset of the ALT_USB_HOST_HCFG register from the beginning of the component. */
#define ALT_USB_HOST_HCFG_OFST 0x0The HCFG register should be at offset 0x400, as shown in document: https://docs.altera.com/v/u/resources/r642178/cyclone-v-hps-register-map
- ScottP_Altera27 days ago
New Contributor
I agree that it looks wrong. However, it depends on what passed into ALT_USB_HOST_HCFG_ADDR(). Have you checked the value in a debugger?
- TruHy27 days ago
New Contributor
You are right, just noticed only a few base registers are used, host and device registers are not used from that file, instead using registers from the tinyusb dwc2 files, which are correct.
- TruHy26 days ago
New Contributor
I had a go at compiling your tinyusb cyclonev ported code, seems you are using an older altera hwlib and older tinyusb, I had to adjust it for the last version of hwlib and tinyusb.
I get the same issue as in your first post. Looking at my own custom USB stack I do see why this is happening. In older dwc2 USB controllers, when enabling a transfer for the EP0 OUT, they want the clear NAK to be disabled for the SETUP stage and STATUS OUT stage only, but not the DATA OUT stage. This quirk causes a bug in the USB controller, specifically it causes the later EP0 transfer to never complete, i.e. always NAK - as you described.
Unfortunately, I could not find a way to fix this, tinyusb does not store the exact control stage in a variable or flag, it treats the SETUP stage as a DATA OUT stage!
If you want to look into fixing it...
See tinyusb/src/device/usbd.c there are two functions of interest:
status_stage_xact()
data_stage_xact()There is no setup_stage_xact() function, instead the data_stage_xact() is used for both the DATA stage and SETUP stage. We are not able to distinguish the exact stage for the OUT direction, i.e. whether it is DATA or SETUP stage. If we could know that the fix goes something like this:
if epnum == 0 and dir == OUT and (stage == SETUP or stage == STATUS) then depctl.clear_nak = 1 else depctl.clear_nak = 0See also tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c
edpt_schedule_packets()depctl.clear_nak = 1
This is a constant, a fix would need that as an input parameter.
In the linux kernel, you can see clearly that they don't clear the NAK for these stages:
https://github.com/torvalds/linux/blob/master/drivers/usb/dwc2/gadget.c#L1211