Merge pull request #2576 from HiFiPhile/dwc2_dma

DWC2 DMA support
This commit is contained in:
Ha Thach 2024-09-25 10:16:28 +07:00 committed by GitHub
commit c8ab65fbb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 400 additions and 284 deletions

View File

@ -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/ :

View File

@ -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;i<len;i++) {

View File

@ -524,7 +524,7 @@
#define TUP_DCD_EDPT_ISO_ALLOC
#endif
#if defined(TUP_USBIP_DWC2)
#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA == 0
#define TUP_MEM_CONST_ADDR
#endif

View File

@ -67,19 +67,7 @@
// Debug level for DWC2
#define DWC2_DEBUG 2
#ifndef dcache_clean
#define dcache_clean(_addr, _size)
#endif
#ifndef dcache_invalidate
#define dcache_invalidate(_addr, _size)
#endif
#ifndef dcache_clean_invalidate
#define dcache_clean_invalidate(_addr, _size)
#endif
static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
static CFG_TUD_MEM_SECTION TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
typedef struct {
uint8_t* buffer;
@ -93,99 +81,230 @@ static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
// EP0 transfers are limited to 1 packet - larger sizes has to be split
static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
static uint16_t _dfifo_top; // top free location in FIFO RAM
// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from dwc2->grxfsiz
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 void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) {
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;
}
//--------------------------------------------------------------------+
// 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);
uint16_t const new_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);
// 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;

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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)
//--------------------------------------------------------------------+

View File

@ -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:

View File

@ -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"}]
}
},