diff --git a/hw/bsp/stm32h7/linker/stm32h743xx_flash.ld b/hw/bsp/stm32h7/linker/stm32h743xx_flash.ld index 336afc01fa..ba18c8cd16 100644 --- a/hw/bsp/stm32h7/linker/stm32h743xx_flash.ld +++ b/hw/bsp/stm32h7/linker/stm32h743xx_flash.ld @@ -32,23 +32,24 @@ /* Entry Point */ ENTRY(Reset_Handler) -/* Highest address of the user mode stack */ -_estack = 0x20020000; /* end of RAM */ -/* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x200; /* required amount of heap */ -_Min_Stack_Size = 0x400; /* required amount of stack */ - /* Specify the memory areas */ MEMORY { -DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K -RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K -RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K -RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K -ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K -FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K + DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K + RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K + RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K + RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K + ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K + } +/* Highest address of the user mode stack */ +_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of RAM */ +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + /* Define output sections */ SECTIONS { @@ -127,7 +128,7 @@ SECTIONS . = ALIGN(4); _edata = .; /* define a global symbol at data end */ - } >DTCMRAM AT> FLASH + } >RAM_D1 AT> FLASH /* Uninitialized data section */ @@ -144,7 +145,9 @@ SECTIONS . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; - } >DTCMRAM + } >RAM_D1 + + /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : @@ -155,9 +158,7 @@ SECTIONS . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); - } >DTCMRAM - - + } >RAM_D1 /* Remove information from the standard libraries */ /DISCARD/ : diff --git a/hw/bsp/xmc4000/family.c b/hw/bsp/xmc4000/family.c index 88810de7a8..c776cb58a1 100644 --- a/hw/bsp/xmc4000/family.c +++ b/hw/bsp/xmc4000/family.c @@ -103,6 +103,12 @@ uint32_t board_button_read(void) { return BUTTON_STATE_ACTIVE == XMC_GPIO_GetInput(BUTTON_PIN); } +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + uint8_t const len = tu_min8(16, max_len); + memcpy(id, g_chipid, len); + return len; +} + int board_uart_read(uint8_t* buf, int len) { #ifdef UART_DEV for(int i=0;igrxfsiz -static uint16_t _allocated_fifo_words_tx; // TX FIFO size in words (IN EPs) +// Number of IN endpoints active +static uint8_t _allocated_ep_in_count; // SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by static bool _sof_en; -// Calculate the RX FIFO size according to minimum recommendations from reference manual -// RxFIFO = (5 * number of control endpoints + 8) + -// ((largest USB packet used / 4) + 1 for status information) + -// (2 * number of OUT endpoints) + 1 for Global NAK -// with number of control endpoints = 1 we have -// RxFIFO = 15 + (largest USB packet used / 4) + 2 * number of OUT endpoints -// we double the largest USB packet size to be able to hold up to 2 packets -static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) { - return 15 + 2 * (max_ep_size / 4) + 2 * ep_count; +//-------------------------------------------------------------------- +// DMA +//-------------------------------------------------------------------- + +TU_ATTR_ALWAYS_INLINE static inline bool dma_enabled(const dwc2_regs_t* dwc2) { + #if !CFG_TUD_DWC2_DMA + (void) dwc2; + return false; + #else + // Internal DMA only + return (dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA); + #endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t dma_cal_epfifo_base(uint8_t rhport) { + // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + return dwc2_controller->ep_fifo_size/4 - 2*dwc2_controller->ep_count; +} + +static void dma_setup_prepare(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) { + if(dwc2->epout[0].doepctl & DOEPCTL_EPENA) { + return; + } + } + + // Receive only 1 packet + dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos); + dwc2->epout[0].doepdma = (uintptr_t)_setup_packet; + dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP; } -TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) { +//--------------------------------------------------------------------+ +// Data FIFO +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) { // flush TX fifo and wait for it cleared dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos); while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} } -TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) { +TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) { // flush RX fifo and wait for it cleared dwc2->grstctl = GRSTCTL_RXFFLSH; while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} } -static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { +/* USB Data FIFO Layout + + The FIFO is split up into + - EPInfo: for storing DMA metadata, only required when use DMA. Maximum size is called + EP_LOC_CNT = ep_fifo_size - ghwcfg3.dfifo_depth. For value less than EP_LOC_CNT, gdfifocfg must be configured before + gahbcfg.dmaen is set + - Buffer mode: 1 word per endpoint direction + - Scatter/Gather DMA: 4 words per endpoint direction + - TX FIFO: one fifo for each IN endpoint. Size is dynamic depending on packet size, starting from top with EP0 IN. + - Shared RX FIFO: a shared fifo for all OUT endpoints. Typically, can hold up to 2 packets of the largest EP size. + + We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is + possible since the free space is located between the RX and TX FIFOs. + + ---------------- ep_fifo_size + | EPInfo | + | for DMA | + |-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) + | IN FIFO 0 | + | control | + |-------------| + | IN FIFO 1 | + |-------------| + | . . . . | + |-------------| + | IN FIFO n | + |-------------| + | FREE | + |-------------|-- GRXFSIZ (expandable) + | OUT FIFO | + | ( Shared ) | + --------------- 0 + + According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): + - Each EP IN needs at least max packet size + - All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula): + - 13 for setup packets + control words (up to 3 setup packets). + - 1 for global NAK (not required/used here). + - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" + - 2 for each used OUT endpoint + + Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum +*/ + +TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { + return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count; +} + +static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + uint8_t const ep_count = dwc2_controller->ep_count; uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); TU_ASSERT(epnum < ep_count); uint16_t fifo_size = tu_div_ceil(packet_size, 4); - - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO 0 | - // --------------- (320 or 1024) - 16 - // | IN FIFO 1 | - // --------------- (320 or 1024) - 16 - x - // | . . . . | - // --------------- (320 or 1024) - 16 - x - y - ... - z - // | IN FIFO MAX | - // --------------- - // | FREE | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // In FIFO is allocated by following rules: - // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n". if (dir == TUSB_DIR_OUT) { // Calculate required size of RX FIFO - uint16_t const sz = calc_grxfsiz(4 * fifo_size, ep_count); - - // If size_rx needs to be extended check if possible and if so enlarge it - if (dwc2->grxfsiz < sz) { - TU_ASSERT(sz + _allocated_fifo_words_tx <= _dwc2_controller[rhport].ep_fifo_size / 4); + uint16_t const new_sz = calc_grxfsiz(4 * fifo_size, ep_count); - // Enlarge RX FIFO - dwc2->grxfsiz = sz; + // If size_rx needs to be extended check if there is enough free space + if (dwc2->grxfsiz < new_sz) { + TU_ASSERT(new_sz <= _dfifo_top); + dwc2->grxfsiz = new_sz; // Enlarge RX FIFO } } else { - // Note if The TXFELVL is configured as half empty. In order - // to be able to write a packet at that point, the fifo must be twice the max_size. + // Check IN endpoints concurrently active limit + if(_dwc2_controller->ep_in_count) { + TU_ASSERT(_allocated_ep_in_count < _dwc2_controller->ep_in_count); + _allocated_ep_in_count++; + } + + // If The TXFELVL is configured as half empty, the fifo must be twice the max_size. if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) { fifo_size *= 2; } // Check if free space is available - TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4); - _allocated_fifo_words_tx += fifo_size; - TU_LOG(DWC2_DEBUG, " Allocated %u bytes at offset %" PRIu32, fifo_size * 4, - _dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4); + TU_ASSERT(_dfifo_top >= fifo_size + dwc2->grxfsiz); + _dfifo_top -= fifo_size; + TU_LOG(DWC2_DEBUG, " TX FIFO %u: allocated %u words at offset %u\r\n", epnum, fifo_size, _dfifo_top); - // DIEPTXF starts at FIFO #1. // Both TXFD and TXSA are in unit of 32-bit words. - dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | - (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); + if (epnum == 0) { + dwc2->dieptxf0 = (fifo_size << DIEPTXF0_TX0FD_Pos) | _dfifo_top; + } else { + // DIEPTXF starts at FIFO #1. + dwc2->dieptxf[epnum - 1] = (fifo_size << DIEPTXF_INEPTXFD_Pos) | _dfifo_top; + } } return true; } +static void dfifo_init(uint8_t rhport) { + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->grxfsiz = calc_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count); + + if(dma_enabled(dwc2)) { + // DMA use last DFIFO to store metadata + _dfifo_top = dma_cal_epfifo_base(rhport); + }else { + _dfifo_top = dwc2_controller->ep_fifo_size / 4; + } + + // Allocate FIFO for EP0 IN + dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE); +} + +// Read a single data packet from receive FIFO +static void dfifo_read_packet(uint8_t rhport, uint8_t* dst, uint16_t len) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile const uint32_t* rx_fifo = dwc2->fifo[0]; + + // Reading full available 32 bit words from fifo + uint16_t full_words = len >> 2; + while (full_words--) { + tu_unaligned_write32(dst, *rx_fifo); + dst += 4; + } + + // Read the remaining 1-3 bytes from fifo + uint8_t const bytes_rem = len & 0x03; + if (bytes_rem != 0) { + uint32_t const tmp = *rx_fifo; + dst[0] = tu_u32_byte0(tmp); + if (bytes_rem > 1) dst[1] = tu_u32_byte1(tmp); + if (bytes_rem > 2) dst[2] = tu_u32_byte2(tmp); + } +} + +// Write a single data packet to EPIN FIFO +static void dfifo_write_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const* src, uint16_t len) { + (void) rhport; + + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num]; + + // Pushing full available 32 bit words to fifo + uint16_t full_words = len >> 2; + while (full_words--) { + *tx_fifo = tu_unaligned_read32(src); + src += 4; + } + + // Write the remaining 1-3 bytes into fifo + uint8_t const bytes_rem = len & 0x03; + if (bytes_rem) { + uint32_t tmp_word = src[0]; + if (bytes_rem > 1) tmp_word |= (src[1] << 8); + if (bytes_rem > 2) tmp_word |= (src[2] << 16); + + *tx_fifo = tmp_word; + } +} + +//-------------------------------------------------------------------- +// Endpoint +//-------------------------------------------------------------------- + static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); @@ -206,7 +325,7 @@ static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoin dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); } else { dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos); - dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum)); + dwc2->daintmsk |= TU_BIT(DAINTMSK_IEPM_Pos + epnum); } } @@ -236,7 +355,7 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { } // Flush the FIFO, and wait until we have confirmed it cleared. - fifo_flush_tx(dwc2, epnum); + dfifo_flush_tx(dwc2, epnum); } else { dwc2_epout_t* epout = dwc2->epout; @@ -272,6 +391,8 @@ static void bus_reset(uint8_t rhport) { _sof_en = false; + _allocated_ep_in_count = 1; + // clear device address dwc2->dcfg &= ~DCFG_DAD_Msk; @@ -287,79 +408,28 @@ static void bus_reset(uint8_t rhport) { } } - fifo_flush_tx(dwc2, 0x10); // all tx fifo - fifo_flush_rx(dwc2); + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); // 3. Set up interrupt mask dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; - // "USB Data FIFOs" section in reference manual - // Peripheral FIFO architecture - // - // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start. - // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located - // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard - // configuration done below. - // - // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed. - // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a - // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually - // opened when the host sends an additional command: setInterface. At this point in time - // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size - // an additional memory - // - // --------------- 320 or 1024 ( 1280 or 4096 bytes ) - // | IN FIFO 0 | - // --------------- (320 or 1024) - 16 - // | IN FIFO 1 | - // --------------- (320 or 1024) - 16 - x - // | . . . . | - // --------------- (320 or 1024) - 16 - x - y - ... - z - // | IN FIFO MAX | - // --------------- - // | FREE | - // --------------- GRXFSIZ - // | OUT FIFO | - // | ( Shared ) | - // --------------- 0 - // - // According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits): - // - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN - // - // - All EP OUT shared a unique OUT FIFO which uses - // - 13 for setup packets + control words (up to 3 setup packets). - // - 1 for global NAK (not required/used here). - // - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" - // - 2 for each used OUT endpoint - // - // Therefore GRXFSIZ = 13 + 1 + 1 + 2 x (Largest-EPsize/4) + 2 x EPOUTnum - // - FullSpeed (64 Bytes ): GRXFSIZ = 15 + 2 x 16 + 2 x ep_count = 47 + 2 x ep_count - // - Highspeed (512 bytes): GRXFSIZ = 15 + 2 x 128 + 2 x ep_count = 271 + 2 x ep_count - // - // NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge - // of the overall picture yet. We will use the worst scenario: largest possible + ep_count - // - // For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO - // are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended. Maybe provide a macro for application to - // overwrite this. - - // EP0 out max is 64 - dwc2->grxfsiz = calc_grxfsiz(64, ep_count); - - // Setup the control endpoint 0 - _allocated_fifo_words_tx = 16; - - // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word ) - dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx); + dfifo_init(rhport); // Fixed control EP0 size to 64 bytes dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos); + dwc2->epout[0].doepctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos); + xfer_status[0][TUSB_DIR_OUT].max_size = 64; xfer_status[0][TUSB_DIR_IN].max_size = 64; - dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + if(dma_enabled(dwc2)) { + dma_setup_prepare(rhport); + } else { + dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + } dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT; } @@ -369,11 +439,11 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c (void) rhport; dwc2_regs_t* dwc2 = DWC2_REG(rhport); + xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir); // EP0 is limited to one packet each xfer // We use multiple transaction of xfer->max_size length to get a whole transfer done if (epnum == 0) { - xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir); total_bytes = tu_min16(ep0_pending[dir], xfer->max_size); ep0_pending[dir] -= total_bytes; } @@ -386,17 +456,31 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); - epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; + if(dma_enabled(dwc2)) { + epin[epnum].diepdma = (uintptr_t)xfer->buffer; - // For ISO endpoint set correct odd/even bit for next frame. - if ((epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); - epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); - } - // Enable fifo empty interrupt only if there are something to put in the fifo. - if (total_bytes != 0) { - dwc2->diepempmsk |= (1 << epnum); + // For ISO endpoint set correct odd/even bit for next frame. + if ((epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { + // Take odd/even bit from frame counter. + uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); + epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); + } + + epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; + } else { + + epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; + + // For ISO endpoint set correct odd/even bit for next frame. + if ((epin[epnum].diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { + // Take odd/even bit from frame counter. + uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); + epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); + } + // Enable fifo empty interrupt only if there are something to put in the fifo. + if (total_bytes != 0) { + dwc2->diepempmsk |= (1 << epnum); + } } } else { dwc2_epout_t* epout = dwc2->epout; @@ -406,13 +490,18 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) | ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk); - epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK; if ((epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 && XFER_CTL_BASE(epnum, dir)->interval == 1) { // Take odd/even bit from frame counter. uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); } + + if(dma_enabled(dwc2)) { + epout[epnum].doepdma = (uintptr_t)xfer->buffer; + } + + epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK; } } @@ -539,7 +628,9 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) - if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) dcfg |= DCFG_XCVRDLY; + if (dwc2->ghwcfg2_bm.hs_phy_type == HS_PHY_TYPE_ULPI) { + dcfg |= DCFG_XCVRDLY; + } dwc2->dcfg = dcfg; } @@ -565,12 +656,9 @@ void dcd_init(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Check Synopsys ID register, failed if controller clock/power is not enabled - if (!check_dwc2(dwc2)) return; + TU_ASSERT(check_dwc2(dwc2), ); dcd_disconnect(rhport); - // max number of endpoints & total_fifo_size are: - // hw_cfg2->num_dev_ep, hw_cfg2->total_fifo_size - if (phy_hs_supported(dwc2)) { phy_hs_init(dwc2); // Highspeed } else { @@ -600,8 +688,8 @@ void dcd_init(uint8_t rhport) { // (non zero-length packet), send STALL back and discard. dwc2->dcfg |= DCFG_NZLSOHSK; - fifo_flush_tx(dwc2, 0x10); // all tx fifo - fifo_flush_rx(dwc2); + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); // Clear all interrupts uint32_t int_mask = dwc2->gintsts; @@ -610,12 +698,21 @@ void dcd_init(uint8_t rhport) { dwc2->gotgint |= int_mask; // Required as part of core initialization. - dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM | - GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; + dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; // Configure TX FIFO empty level for interrupt. Default is complete empty dwc2->gahbcfg |= GAHBCFG_TXFELVL; + if (dma_enabled(dwc2)) { + const uint16_t epinfo_base = dma_cal_epfifo_base(rhport); + dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base; + + // DMA seems to be only settable after a core reset + dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; + }else { + dwc2->gintmsk |= GINTMSK_RXFLVLM; + } + // Enable global interrupt dwc2->gahbcfg |= GAHBCFG_GINT; @@ -718,7 +815,7 @@ void dcd_sof_enable(uint8_t rhport, bool en) { *------------------------------------------------------------------*/ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { - TU_ASSERT(fifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt))); + TU_ASSERT(dfifo_alloc(rhport, desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt))); edpt_activate(rhport, desc_edpt); return true; } @@ -728,6 +825,8 @@ void dcd_edpt_close_all(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); uint8_t const ep_count = _dwc2_controller[rhport].ep_count; + _allocated_ep_in_count = 1; + // Disable non-control interrupt dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos); @@ -745,26 +844,21 @@ void dcd_edpt_close_all(uint8_t rhport) { xfer_status[n][TUSB_DIR_IN].max_size = 0; } - // reset allocated fifo OUT - dwc2->grxfsiz = calc_grxfsiz(64, ep_count); - // reset allocated fifo IN - _allocated_fifo_words_tx = 16; + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); - fifo_flush_tx(dwc2, 0x10); // all tx fifo - fifo_flush_rx(dwc2); + dfifo_init(rhport); // re-init dfifo } bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { - TU_ASSERT(fifo_alloc(rhport, ep_addr, largest_packet_size)); + TU_ASSERT(dfifo_alloc(rhport, ep_addr, largest_packet_size)); return true; } bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { // Disable EP to clear potential incomplete transfers edpt_disable(rhport, p_endpoint_desc->bEndpointAddress, false); - edpt_activate(rhport, p_endpoint_desc); - return true; } @@ -831,6 +925,9 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { edpt_disable(rhport, ep_addr, true); + if((tu_edpt_number(ep_addr) == 0) && dma_enabled(DWC2_REG(rhport))) { + dma_setup_prepare(rhport); + } } void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { @@ -851,56 +948,9 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { } } -/*------------------------------------------------------------------*/ - -// Read a single data packet from receive FIFO -static void read_fifo_packet(uint8_t rhport, uint8_t* dst, uint16_t len) { - (void) rhport; - - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - volatile const uint32_t* rx_fifo = dwc2->fifo[0]; - - // Reading full available 32 bit words from fifo - uint16_t full_words = len >> 2; - while (full_words--) { - tu_unaligned_write32(dst, *rx_fifo); - dst += 4; - } - - // Read the remaining 1-3 bytes from fifo - uint8_t const bytes_rem = len & 0x03; - if (bytes_rem != 0) { - uint32_t const tmp = *rx_fifo; - dst[0] = tu_u32_byte0(tmp); - if (bytes_rem > 1) dst[1] = tu_u32_byte1(tmp); - if (bytes_rem > 2) dst[2] = tu_u32_byte2(tmp); - } -} - -// Write a single data packet to EPIN FIFO -static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const* src, uint16_t len) { - (void) rhport; - - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num]; - - // Pushing full available 32 bit words to fifo - uint16_t full_words = len >> 2; - while (full_words--) { - *tx_fifo = tu_unaligned_read32(src); - src += 4; - } - - // Write the remaining 1-3 bytes into fifo - uint8_t const bytes_rem = len & 0x03; - if (bytes_rem) { - uint32_t tmp_word = src[0]; - if (bytes_rem > 1) tmp_word |= (src[1] << 8); - if (bytes_rem > 2) tmp_word |= (src[2] << 16); - - *tx_fifo = tmp_word; - } -} +//-------------------------------------------------------------------- +// Interrupt Handler +//-------------------------------------------------------------------- static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -954,7 +1004,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); } else { // Linear buffer - read_fifo_packet(rhport, xfer->buffer, bcnt); + dfifo_read_packet(rhport, xfer->buffer, bcnt); // Increment pointer to xfer data xfer->buffer += bcnt; @@ -978,21 +1028,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { // XFRC complete is additionally generated when // - setup packet is received // - complete the data stage of control write is complete - if ((epnum == 0) && (bcnt == 0) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) { - uint32_t doepint = epout->doepint; - - if (doepint & (DOEPINT_STPKTRX | DOEPINT_OTEPSPR)) { - // skip this "no-data" transfer complete event - // Note: STPKTRX will be clear later by setup received handler - uint32_t clear_flags = DOEPINT_XFRC; - - if (doepint & DOEPINT_OTEPSPR) clear_flags |= DOEPINT_OTEPSPR; - - epout->doepint = clear_flags; - - // TU_LOG(DWC2_DEBUG, " FIX extra transfer complete on setup/data compete\r\n"); - } - } + // It will be handled in handle_epout_irq() break; default: // Invalid @@ -1013,18 +1049,7 @@ static void handle_epout_irq(uint8_t rhport) { uint32_t const doepint = epout->doepint; - // SETUP packet Setup Phase done. - if (doepint & DOEPINT_STUP) { - uint32_t clear_flag = DOEPINT_STUP; - - // STPKTRX is only available for version from 3_00a - if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) { - clear_flag |= DOEPINT_STPKTRX; - } - - epout->doepint = clear_flag; - dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); - } + TU_ASSERT((epout->doepint & DOEPINT_AHBERR) == 0, ); // OUT XFER complete if (epout->doepint & DOEPINT_XFRC) { @@ -1032,14 +1057,66 @@ static void handle_epout_irq(uint8_t rhport) { xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); - // EP0 can only handle one packet - if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) { - // Schedule another packet to be received. - edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); + if(dma_enabled(dwc2)) { + if (doepint & DOEPINT_STUP) { + // STPKTRX is only available for version from 3_00a + if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) { + epout->doepint = DOEPINT_STPKTRX; + } + } else if (doepint & DOEPINT_OTEPSPR) { + epout->doepint = DOEPINT_OTEPSPR; + } else { + if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) { + epout->doepint = DOEPINT_STPKTRX; + } else { + // EP0 can only handle one packet + if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) { + // Schedule another packet to be received. + edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); + } else { + // Fix packet length + uint16_t remain = (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; + xfer->total_len -= remain; + // this is ZLP, so prepare EP0 for next setup + if(n == 0 && xfer->total_len == 0) { + dma_setup_prepare(rhport); + } + + dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } + } } else { - dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); + if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid == DWC2_CORE_REV_3_10a)) { + epout->doepint = DOEPINT_STPKTRX; + } else { + if ((doepint & DOEPINT_OTEPSPR) && (dwc2->gsnpsid == DWC2_CORE_REV_3_10a)) { + epout->doepint = DOEPINT_OTEPSPR; + } + + // EP0 can only handle one packet + if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) { + // Schedule another packet to be received. + edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); + } else { + dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } } } + + // SETUP packet Setup Phase done. + if (doepint & DOEPINT_STUP) { + epout->doepint = DOEPINT_STUP; + if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) { + epout->doepint = DOEPINT_STPKTRX; + } + if(dma_enabled(dwc2) && (dwc2->gsnpsid > DWC2_CORE_REV_3_00a)) { + dma_setup_prepare(rhport); + } + + dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); + } } } } @@ -1064,6 +1141,9 @@ static void handle_epin_irq(uint8_t rhport) { // Schedule another packet to be transmitted. edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); } else { + if((n == 0) && dma_enabled(dwc2)) { + dma_setup_prepare(rhport); + } dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); } } @@ -1093,7 +1173,7 @@ static void handle_epin_irq(uint8_t rhport) { volatile uint32_t* tx_fifo = dwc2->fifo[n]; tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size); } else { - write_fifo_packet(rhport, n, xfer->buffer, packet_size); + dfifo_write_packet(rhport, n, xfer->buffer, packet_size); // Increment pointer to xfer data xfer->buffer += packet_size; diff --git a/src/portable/synopsys/dwc2/dwc2_bcm.h b/src/portable/synopsys/dwc2/dwc2_bcm.h index 732d96ae09..e5824606ad 100644 --- a/src/portable/synopsys/dwc2/dwc2_bcm.h +++ b/src/portable/synopsys/dwc2/dwc2_bcm.h @@ -39,7 +39,7 @@ static const dwc2_controller_t _dwc2_controller[] = { - { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 4096 } + { .reg_base = USB_OTG_GLOBAL_BASE, .irqnum = USB_IRQn, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 16384 } }; #define dcache_clean(_addr, _size) data_clean(_addr, _size) diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index 1a938ea46c..921cee29e7 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -39,10 +39,11 @@ #include "soc/usb_wrap_struct.h" #define DWC2_REG_BASE 0x60080000UL -#define DWC2_EP_MAX 6 // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN) +#define DWC2_EP_MAX 7 +#define DWC2_EP_IN_MAX 5 static const dwc2_controller_t _dwc2_controller[] = { - { .reg_base = DWC2_REG_BASE, .irqnum = 0, .ep_count = DWC2_EP_MAX, .ep_fifo_size = 1024 } + { .reg_base = DWC2_REG_BASE, .irqnum = 0, .ep_count = DWC2_EP_MAX, .ep_in_count = DWC2_EP_IN_MAX, .ep_fifo_size = 1024 } }; static intr_handle_t usb_ih; diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index cb694b3267..c21bf9e941 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -46,6 +46,7 @@ typedef struct uintptr_t reg_base; uint32_t irqnum; uint8_t ep_count; + uint8_t ep_in_count; uint32_t ep_fifo_size; }dwc2_controller_t; @@ -99,6 +100,12 @@ enum { FS_PHY_TYPE_ULPI, }; +enum { + GHWCFG2_ARCH_SLAVE_ONLY = 0, + GHWCFG2_ARCH_EXTERNAL_DMA, // 1 + GHWCFG2_ARCH_INTERNAL_DMA, // 2 +}; + typedef struct TU_ATTR_PACKED { uint32_t op_mode : 3; // 0: HNP and SRP | 1: SRP | 2: non-HNP, non-SRP @@ -133,7 +140,7 @@ typedef struct TU_ATTR_PACKED uint32_t otg_enable_hsic : 1; // 1: HSIC-capable with shared UTMI PHY interface | 0: non-HSIC uint32_t battery_charger_support : 1; // support battery charger uint32_t lpm_mode : 1; // LPC mode - uint32_t total_fifo_size : 16; // DFIFO depth value in terms of 32-bit words + uint32_t dfifo_depth : 16; // DFIFO depth - EP_LOC_CNT in terms of 32-bit words }dwc2_ghwcfg3_t; TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg3_t) == 4, "incorrect size"); @@ -230,15 +237,15 @@ union { volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep) union { volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2 - dwc2_ghwcfg2_t ghwcfg2_bm; + volatile dwc2_ghwcfg2_t ghwcfg2_bm; }; union { volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3 - dwc2_ghwcfg3_t ghwcfg3_bm; + volatile dwc2_ghwcfg3_t ghwcfg3_bm; }; union { volatile uint32_t ghwcfg4; // 050 User Hardware Configuration4 - dwc2_ghwcfg4_t ghwcfg4_bm; + volatile dwc2_ghwcfg4_t ghwcfg4_bm; }; volatile uint32_t glpmcfg; // 054 Core LPM Configuration volatile uint32_t gpwrdn; // 058 Power Down @@ -1239,6 +1246,12 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GLPMCFG_ENBESL_Msk (0x1UL << GLPMCFG_ENBESL_Pos) // 0x10000000 #define GLPMCFG_ENBESL GLPMCFG_ENBESL_Msk // Enable best effort service latency +// GDFIFOCFG +#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16) +#define GDFIFOCFG_EPINFOBASE_SHIFT 16 +#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0) +#define GDFIFOCFG_GDFIFOCFG_SHIFT 0 + /******************** Bit definition for DIEPEACHMSK1 register ********************/ #define DIEPEACHMSK1_XFRCM_Pos (0U) #define DIEPEACHMSK1_XFRCM_Msk (0x1UL << DIEPEACHMSK1_XFRCM_Pos) // 0x00000001 diff --git a/src/tusb_option.h b/src/tusb_option.h index fb0209023d..17de9cd72b 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -228,7 +228,7 @@ #define OPT_MODE_SPEED_MASK 0xff00 //--------------------------------------------------------------------+ -// Include tusb_config.h and tusb_mcu.h +// Include tusb_config.h //--------------------------------------------------------------------+ // Allow to use command line to change the config name/location @@ -238,6 +238,33 @@ #include "tusb_config.h" #endif +//--------------------------------------------------------------------+ +// USBIP +//--------------------------------------------------------------------+ + +// DWC2 controller: use DMA for data transfer +// For processors with data cache enabled, USB endpoint buffer region +// (defined by CFG_TUSB_MEM_SECTION) must be declared as non-cacheable. +// For example, on Cortex-M7 the MPU region can be configured as normal +// non-cacheable, with RASR register value: TEX=1 C=0 B=0 S=0. +#ifndef CFG_TUD_DWC2_DMA + #define CFG_TUD_DWC2_DMA 0 +#endif + +// Enable PIO-USB software host controller +#ifndef CFG_TUH_RPI_PIO_USB + #define CFG_TUH_RPI_PIO_USB 0 +#endif + +#ifndef CFG_TUD_RPI_PIO_USB + #define CFG_TUD_RPI_PIO_USB 0 +#endif + +// MAX3421 Host controller option +#ifndef CFG_TUH_MAX3421 + #define CFG_TUH_MAX3421 0 +#endif + #include "common/tusb_mcu.h" //-------------------------------------------------------------------- @@ -548,20 +575,6 @@ #define CFG_TUH_API_EDPT_XFER 0 #endif -// Enable PIO-USB software host controller -#ifndef CFG_TUH_RPI_PIO_USB - #define CFG_TUH_RPI_PIO_USB 0 -#endif - -#ifndef CFG_TUD_RPI_PIO_USB - #define CFG_TUD_RPI_PIO_USB 0 -#endif - -// MAX3421 Host controller option -#ifndef CFG_TUH_MAX3421 - #define CFG_TUH_MAX3421 0 -#endif - //--------------------------------------------------------------------+ // TypeC Options (Default) //--------------------------------------------------------------------+ diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index a569e76665..ec28328d47 100644 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -411,6 +411,7 @@ def test_board(board): for skip in board_tests['skip']: if skip in test_list: test_list.remove(skip) + print(f'{name:25} {skip:30} ... Skip') err_count = 0 for test in test_list: diff --git a/test/hil/rpi.json b/test/hil/rpi.json index 77919f3460..672e857fdf 100644 --- a/test/hil/rpi.json +++ b/test/hil/rpi.json @@ -59,6 +59,7 @@ "flasher_sn": "E6633861A3978538", "flasher_args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\"", "tests": { + "skip": ["dual/host_info_to_device_cdc"], "dual_attached": [{"vid_pid": "1a86_55d4", "serial": "533D004242"}] } },