mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 11:02:16 +00:00
rusb2: EP0 OUT reliability, HS UTMI PHY power-up, FS-only build support
- EP0 OUT: park a back-to-back data-stage packet the DCP accepted before PID could go NAK and deliver it into the next armed chunk; flow-control the single-buffer control pipe between chunks (usbtest ctrl_out corruption); discard a packet parked while an OUT pipe was halted so BOT reset recovery's fresh CBW read can't receive stale WRITE data - HS UTMI PHY power-up per the FSP sequence, shared by dcd/hcd: CLKSEL programmed from the board XTAL (EK-RA8M1 runs 20 MHz; the 24 MHz reset default never locks) while DIRPD holds the PHY down, then timed release - hw/bsp(ra8m1_ek): fix U60CK divider macro - BSP_CFG_U60CK_DIV used the generic USB_CLOCK_DIV_8 encoding (7), which USB60CKDIVCR rejects, leaving the USBHS link domain at 480 MHz; the USB60-specific BSP_CLOCKS_USB60_CLOCK_DIV_8 (4) sticks and yields the required 60 MHz from PLL1P - support FS-only builds on the high-speed port: gate SYSCFG.HSE on TUD_OPT_HIGH_SPEED (RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED was a silent no-op) and always compile both hwfifo access widths - the FIFO width belongs to the module, not the link speed (FS builds corrupted odd-length tails: 16-bit access against MBW-32) - iso activate: reset stale pipe bookkeeping so a BRDY firing before the class re-arms can't replay a pre-SET_INTERFACE transfer; write PIPEBUF after PIPESEL selects the pipe (PIPESEL-windowed register) - clear-halt: re-assert BUF on a still-armed OUT pipe (usbtest case 29) - bound the D0FIFO ready spin so an undrained double-buffered IN pipe can't freeze the stack with the IRQ masked - usbtest example: cap interrupt mps at 64 on RUSB2 high speed (pipes 6-9 have a fixed 64-byte buffer, RA6M5 UM 29.1) Verified: usbtest 30/30 on ra6m5_ek (HS), ra4m1_ek (FS) and ra8m1_ek (FS-forced build on the HS port). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
This commit is contained in:
@ -57,7 +57,13 @@
|
||||
#define USBTEST_INT_EP_MPS_FS 64
|
||||
#define USBTEST_ISO_EP_MPS_FS 128
|
||||
#endif
|
||||
#define USBTEST_INT_EP_MPS_HS 512
|
||||
// RUSB2 (Renesas RA) interrupt pipes 6-9 have a fixed 64-byte single buffer at any speed
|
||||
// (RA6M5 UM R01UH0891 sec 29.1: "Pipes 6 to 9: Interrupt transfer with 64-byte single buffer").
|
||||
#if TU_CHECK_MCU(OPT_MCU_RAXXX)
|
||||
#define USBTEST_INT_EP_MPS_HS 64
|
||||
#else
|
||||
#define USBTEST_INT_EP_MPS_HS 512
|
||||
#endif
|
||||
#define USBTEST_ISO_EP_MPS_HS 512
|
||||
|
||||
// Compile-time capability maximum: sizes the source buffers / vendor epbufs for the largest
|
||||
|
||||
@ -51,6 +51,9 @@
|
||||
#define BSP_CFG_CANFDCLK_DIV (BSP_CLOCKS_CANFD_CLOCK_DIV_8) /* CANFDCLK Div /8 */
|
||||
#define BSP_CFG_I3CCLK_DIV (BSP_CLOCKS_I3C_CLOCK_DIV_3) /* I3CCLK Div /3 */
|
||||
#define BSP_CFG_UCK_DIV (BSP_CLOCKS_USB_CLOCK_DIV_5) /* UCK Div /5 */
|
||||
#define BSP_CFG_U60CK_DIV (BSP_CLOCKS_USB_CLOCK_DIV_8) /* U60CK Div /8 */
|
||||
/* U60CK Div /8: PLL1P 480 MHz -> 60 MHz. Hand-fixed: Smart Configurator emitted the USB_ macro
|
||||
* namespace (BSP_CLOCKS_USB_CLOCK_DIV_8 = 7, rejected by USB60CKDIVCR -> link clock ran at
|
||||
* 480 MHz); configuration.xml already says u60ck.div.8, so keep the USB60_ macro if regenerating. */
|
||||
#define BSP_CFG_U60CK_DIV (BSP_CLOCKS_USB60_CLOCK_DIV_8)
|
||||
#define BSP_CFG_OCTA_DIV (BSP_CLOCKS_OCTA_CLOCK_DIV_4) /* OCTASPICLK Div /4 */
|
||||
#endif /* BSP_CLOCK_CFG_H_ */
|
||||
|
||||
@ -43,6 +43,7 @@ typedef struct
|
||||
|
||||
static dcd_data_t _dcd;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
@ -190,6 +191,15 @@ static bool pipe0_xfer_out(rusb2_reg_t *rusb) {
|
||||
pipe_state_t *pipe = &_dcd.pipe[0];
|
||||
const unsigned rem = pipe->remaining;
|
||||
|
||||
// BRDY with no armed transfer: a back-to-back data-stage packet beat the PID=NAK below (the
|
||||
// host has already ACKed it). Park it in the DCP buffer — an unread buffer NAKs further OUTs —
|
||||
// and let process_pipe0_xfer deliver it when usbd arms the next chunk. BCLR here would silently
|
||||
// drop the packet and shift every later chunk by one (usbtest ctrl_out corruption at ra4m1).
|
||||
if (pipe->buf == NULL && rem == 0) {
|
||||
rusb->DCPCTR = RUSB2_PIPE_CTR_PID_NAK;
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t mps = edpt0_max_packet_size(rusb);
|
||||
const uint16_t vld = rusb->CFIFOCTR_b.DTLN;
|
||||
const uint16_t len = tu_min16(tu_min16(rem, mps), vld);
|
||||
@ -360,7 +370,16 @@ static void process_status_completion(uint8_t rhport)
|
||||
dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, true);
|
||||
}
|
||||
|
||||
static bool process_pipe0_xfer(rusb2_reg_t *rusb, int buffer_type, uint8_t ep_addr, void *buffer,
|
||||
// Report a completed transfer on `num` and reset its bookkeeping. Single completion path for the
|
||||
// BRDY handler and the EP0 parked-packet drain, so they can't diverge (e.g. on clearing `queued`).
|
||||
static void pipe_xfer_complete(uint8_t rhport, unsigned num, bool in_isr) {
|
||||
pipe_state_t *pipe = &_dcd.pipe[num];
|
||||
pipe->queued = false;
|
||||
dcd_event_xfer_complete(rhport, pipe->ep, pipe->length - pipe->remaining,
|
||||
XFER_RESULT_SUCCESS, in_isr);
|
||||
}
|
||||
|
||||
static bool process_pipe0_xfer(uint8_t rhport, rusb2_reg_t *rusb, int buffer_type, uint8_t ep_addr, void *buffer,
|
||||
uint16_t total_bytes) {
|
||||
uint16_t fifo_sel =
|
||||
(rusb2_is_highspeed_reg(rusb) ? RUSB2_FIFOSEL_MBW_32BIT : RUSB2_FIFOSEL_MBW_16BIT) | FIFOSEL_BIGEND;
|
||||
@ -386,6 +405,15 @@ static bool process_pipe0_xfer(rusb2_reg_t *rusb, int buffer_type, uint8_t ep_ad
|
||||
/* IN */
|
||||
TU_ASSERT(rusb->DCPCTR_b.BSTS && (rusb->USBREQ & 0x80));
|
||||
pipe0_xfer_in(rusb);
|
||||
} else if (rusb->CFIFOCTR_b.DTLN > 0) {
|
||||
/* OUT: a back-to-back packet parked by pipe0_xfer_out already sits in the DCP buffer (its
|
||||
BRDY has fired and been cleared) — deliver it into this chunk now; no new BRDY will come
|
||||
for it. Runs with the USB IRQ masked (dcd_edpt_xfer). Detected via the hardware DTLN
|
||||
rather than a driver flag: the BCLR at SETUP/bus-reset then self-heals any parked state. */
|
||||
if (pipe0_xfer_out(rusb)) {
|
||||
pipe_xfer_complete(rhport, 0, false);
|
||||
return true; // PID stays NAK (set by pipe0_xfer_out) until the next chunk is armed
|
||||
}
|
||||
}
|
||||
rusb->DCPCTR = RUSB2_PIPE_CTR_PID_BUF;
|
||||
} else {
|
||||
@ -460,11 +488,11 @@ static bool process_pipe_xfer(rusb2_reg_t* rusb, int buffer_type, uint8_t ep_add
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool process_edpt_xfer(rusb2_reg_t* rusb, int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
|
||||
static bool process_edpt_xfer(uint8_t rhport, rusb2_reg_t* rusb, int buffer_type, uint8_t ep_addr, void* buffer, uint16_t total_bytes)
|
||||
{
|
||||
const unsigned epn = tu_edpt_number(ep_addr);
|
||||
if (0 == epn) {
|
||||
return process_pipe0_xfer(rusb, buffer_type, ep_addr, buffer, total_bytes);
|
||||
return process_pipe0_xfer(rhport, rusb, buffer_type, ep_addr, buffer, total_bytes);
|
||||
} else {
|
||||
return process_pipe_xfer(rusb, buffer_type, ep_addr, buffer, total_bytes);
|
||||
}
|
||||
@ -508,10 +536,7 @@ static void process_pipe_brdy(uint8_t rhport, unsigned num)
|
||||
}
|
||||
}
|
||||
if (completed) {
|
||||
pipe->queued = false;
|
||||
dcd_event_xfer_complete(rhport, pipe->ep,
|
||||
pipe->length - pipe->remaining,
|
||||
XFER_RESULT_SUCCESS, true);
|
||||
pipe_xfer_complete(rhport, num, true);
|
||||
// TU_LOG1("C %d %d\r\n", num, pipe->length - pipe->remaining);
|
||||
}
|
||||
}
|
||||
@ -636,19 +661,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
||||
|
||||
#ifdef RUSB2_SUPPORT_HIGHSPEED
|
||||
if ( rusb2_is_highspeed_rhport(rhport) ) {
|
||||
rusb->SYSCFG_b.HSE = 1;
|
||||
|
||||
// leave CLKSEL as default (0x11) 24Mhz
|
||||
|
||||
// Power and reset UTMI Phy
|
||||
uint16_t physet = (rusb->PHYSET | RUSB2_PHYSET_PLLRESET_Msk) & ~RUSB2_PHYSET_DIRPD_Msk;
|
||||
rusb->PHYSET = physet;
|
||||
R_BSP_SoftwareDelay((uint32_t) 1, BSP_DELAY_UNITS_MILLISECONDS);
|
||||
rusb->PHYSET_b.PLLRESET = 0;
|
||||
|
||||
// set UTMI to operating mode and wait for PLL lock confirmation
|
||||
rusb->LPSTS_b.SUSPENDM = 1;
|
||||
while (!rusb->PLLSTA_b.PLLLOCK) {}
|
||||
rusb->SYSCFG_b.HSE = TUD_OPT_HIGH_SPEED ? 1 : 0; // FS-only build: no HS chirp
|
||||
rusb2_utmi_phy_powerup(rusb);
|
||||
|
||||
rusb->SYSCFG_b.DRPD = 0;
|
||||
rusb->SYSCFG_b.USBE = 1;
|
||||
@ -753,6 +767,10 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
|
||||
if ( !rusb2_is_highspeed_rhport(rhport) && mps > 256) {
|
||||
return false;
|
||||
}
|
||||
} else if (xfer == TUSB_XFER_INTERRUPT) {
|
||||
// Interrupt pipes (6-9) have a fixed 64-byte buffer even in high speed (RA6M5 UM 29.1);
|
||||
// a larger PIPEMAXP would enumerate, then silently truncate every transfer
|
||||
TU_ASSERT(mps <= 64);
|
||||
}
|
||||
|
||||
// Re-opening an endpoint must reuse its pipe: usbd_edpt_close() is a no-op on ISO_ALLOC ports,
|
||||
@ -770,13 +788,12 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
|
||||
/* setup pipe */
|
||||
dcd_int_disable(rhport);
|
||||
|
||||
rusb->PIPESEL = num;
|
||||
if ( rusb2_is_highspeed_rhport(rhport) ) {
|
||||
// FIXME shouldn't be after pipe selection and config, also the BUFNMB should be changed
|
||||
// depending on the allocation scheme
|
||||
// PIPEBUF is PIPESEL-windowed (RA6M5 UM 29.2.35): write it after selecting the pipe.
|
||||
// FIXME BUFNMB is a fixed 0x08 for every pipe; a real per-pipe allocation scheme is needed.
|
||||
rusb->PIPEBUF = 0x7C08;
|
||||
}
|
||||
|
||||
rusb->PIPESEL = num;
|
||||
rusb->PIPEMAXP = mps;
|
||||
volatile uint16_t *ctr = get_pipectr(rusb, num);
|
||||
*ctr = RUSB2_PIPE_CTR_ACLRM_Msk | RUSB2_PIPE_CTR_SQCLR_Msk;
|
||||
@ -860,14 +877,12 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet
|
||||
_dcd.ep[dir][epn] = num;
|
||||
|
||||
dcd_int_disable(rhport);
|
||||
rusb->PIPESEL = (uint16_t) num;
|
||||
if (rusb2_is_highspeed_rhport(rhport)) {
|
||||
// FIXME (as in dcd_edpt_open): PIPEBUF is a PIPESEL-windowed register (RA6M5 UM §29.2.35) so it
|
||||
// must be written AFTER PIPESEL selects this pipe, and the fixed BUFNMB=0x08 overlaps every
|
||||
// HS pipe — a real per-pipe buffer allocator is needed. Left as-is: no RA6M5 HS board on
|
||||
// the HIL rig to validate a change, and the current mis-ordered write is inert on FS/RA4M1.
|
||||
// PIPEBUF is PIPESEL-windowed (RA6M5 UM 29.2.35): write it after selecting the pipe.
|
||||
// FIXME (as in dcd_edpt_open): BUFNMB is a fixed 0x08 for every pipe; a real allocator is needed.
|
||||
rusb->PIPEBUF = 0x7C08;
|
||||
}
|
||||
rusb->PIPESEL = (uint16_t) num;
|
||||
rusb->PIPEMAXP = largest_packet_size;
|
||||
volatile uint16_t *ctr = get_pipectr(rusb, num);
|
||||
*ctr = RUSB2_PIPE_CTR_ACLRM_Msk | RUSB2_PIPE_CTR_SQCLR_Msk;
|
||||
@ -893,6 +908,13 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep)
|
||||
volatile uint16_t *ctr = get_pipectr(rusb, num);
|
||||
*ctr = RUSB2_PIPE_CTR_ACLRM_Msk | RUSB2_PIPE_CTR_SQCLR_Msk; // abort in-flight + reset data toggle
|
||||
*ctr = 0;
|
||||
// a transfer armed before SET_INTERFACE survives to here (no dcd close on this port): drop the
|
||||
// stale bookkeeping so a BRDY firing before the class re-arms can't replay it
|
||||
pipe_state_t *pipe = &_dcd.pipe[num];
|
||||
pipe->buf = NULL;
|
||||
pipe->remaining = 0;
|
||||
pipe->queued = false;
|
||||
pipe->zlp_pending = false;
|
||||
*ctr = RUSB2_PIPE_CTR_PID_BUF; // enable
|
||||
dcd_int_enable(rhport);
|
||||
return true;
|
||||
@ -904,7 +926,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t
|
||||
rusb2_reg_t* rusb = RUSB2_REG(rhport);
|
||||
|
||||
dcd_int_disable(rhport);
|
||||
bool r = process_edpt_xfer(rusb, 0, ep_addr, buffer, total_bytes);
|
||||
bool r = process_edpt_xfer(rhport, rusb, 0, ep_addr, buffer, total_bytes);
|
||||
dcd_int_enable(rhport);
|
||||
|
||||
return r;
|
||||
@ -917,7 +939,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_
|
||||
rusb2_reg_t* rusb = RUSB2_REG(rhport);
|
||||
|
||||
dcd_int_disable(rhport);
|
||||
bool r = process_edpt_xfer(rusb, 1, ep_addr, ff, total_bytes);
|
||||
bool r = process_edpt_xfer(rhport, rusb, 1, ep_addr, ff, total_bytes);
|
||||
dcd_int_enable(rhport);
|
||||
|
||||
return r;
|
||||
@ -952,6 +974,12 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
|
||||
} else {
|
||||
const unsigned num = _dcd.ep[0][tu_edpt_number(ep_addr)];
|
||||
rusb->PIPESEL = (uint16_t)num;
|
||||
// Drop any packet parked in the buffer while halted: a data-OUT packet the host sent before
|
||||
// aborting its transfer would otherwise be delivered into the next read after recovery
|
||||
// (BOT reset + clear-halt re-arms a 31-byte CBW read which then receives stale WRITE data,
|
||||
// "SCSI CBW is not valid" -> stall -> reset loop; ra6m5 msc write wedge).
|
||||
*ctr = RUSB2_PIPE_CTR_ACLRM_Msk;
|
||||
*ctr = 0;
|
||||
// Non-bulk OUT re-enables straight away. Bulk OUT is normally armed together with its transaction
|
||||
// counter (TRE) by process_pipe_xfer(), so we don't blindly re-enable it here — but if a receive
|
||||
// was already armed (still queued), SQCLR above just left it NAKing. Re-assert BUF so it keeps
|
||||
|
||||
@ -454,11 +454,9 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
||||
if (rusb2_is_highspeed_rhport(rhport) ) {
|
||||
rusb->SYSCFG_b.HSE = 1;
|
||||
rusb->PHYSET_b.HSEB = 0;
|
||||
rusb->PHYSET_b.DIRPD = 0;
|
||||
R_BSP_SoftwareDelay((uint32_t) 1, BSP_DELAY_UNITS_MILLISECONDS);
|
||||
rusb->PHYSET_b.PLLRESET = 0;
|
||||
rusb->LPSTS_b.SUSPENDM = 1;
|
||||
while ( !rusb->PLLSTA_b.PLLLOCK );
|
||||
// same PHY reference-clock + power-up requirements as dcd_init: without CLKSEL matching the
|
||||
// board XTAL the PLL never locks and the wait below would spin forever (e.g. EK-RA8M1, 20 MHz)
|
||||
rusb2_utmi_phy_powerup(rusb);
|
||||
rusb->SYSCFG_b.DRPD = 1;
|
||||
rusb->SYSCFG_b.DCFM = 1;
|
||||
rusb->SYSCFG_b.DPRPU = 0;
|
||||
|
||||
@ -49,6 +49,23 @@ typedef struct {
|
||||
|
||||
#define rusb2_is_highspeed_rhport(_p) (_p == 1)
|
||||
#define rusb2_is_highspeed_reg(_reg) (_reg == RUSB2_REG(1))
|
||||
|
||||
// UTMI PHY reference clock is the main oscillator: PHYSET.CLKSEL must match the board XTAL
|
||||
// before the PHY PLL is released (RA6M5 UM R01UH0891 29.2.17: 00=12 MHz, 10=20 MHz,
|
||||
// 11=24 MHz reset default). EK-RA6M5 runs 24 MHz (default works); EK-RA8M1 runs 20 MHz and
|
||||
// never locks/chirps on the default. A board with a non-standard USB clocking scheme can
|
||||
// pre-define RUSB2_PHYSET_CLKSEL_VALUE to override this selection.
|
||||
#ifndef RUSB2_PHYSET_CLKSEL_VALUE
|
||||
#if BSP_CFG_XTAL_HZ == 12000000
|
||||
#define RUSB2_PHYSET_CLKSEL_VALUE 0u
|
||||
#elif BSP_CFG_XTAL_HZ == 20000000
|
||||
#define RUSB2_PHYSET_CLKSEL_VALUE 2u
|
||||
#elif BSP_CFG_XTAL_HZ == 24000000
|
||||
#define RUSB2_PHYSET_CLKSEL_VALUE 3u
|
||||
#else
|
||||
#error "USBHS UTMI PHY: no PHYSET.CLKSEL encoding for this BSP_CFG_XTAL_HZ; define RUSB2_PHYSET_CLKSEL_VALUE"
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#define RUSB2_CONTROLLER_COUNT 1
|
||||
|
||||
@ -84,6 +101,31 @@ TU_ATTR_ALWAYS_INLINE static inline void rusb2_int_disable(uint8_t rhport) {
|
||||
TU_ATTR_ALWAYS_INLINE static inline void rusb2_phy_init(void) {
|
||||
}
|
||||
|
||||
#ifdef RUSB2_SUPPORT_HIGHSPEED
|
||||
// UTMI PHY power-up per the FSP reference sequence (r_usb_preg_access.c), shared by dcd_init and
|
||||
// hcd_init: program CLKSEL to the board XTAL while the PHY is powered down (DIRPD=1), 1 us,
|
||||
// release DIRPD, 1 ms, release PLLRESET, then wait for PLL lock. Changing CLKSEL as the PHY
|
||||
// powers up gets mis-sampled (EK-RA8M1, 20 MHz).
|
||||
static inline void rusb2_utmi_phy_powerup(rusb2_reg_t* rusb) {
|
||||
uint16_t physet = rusb->PHYSET | RUSB2_PHYSET_DIRPD_Msk;
|
||||
rusb->PHYSET = physet;
|
||||
#ifdef RUSB2_PHYSET_CLKSEL_VALUE
|
||||
physet = (uint16_t) ((physet & ~RUSB2_PHYSET_CLKSEL_Msk) |
|
||||
(RUSB2_PHYSET_CLKSEL_VALUE << RUSB2_PHYSET_CLKSEL_Pos));
|
||||
rusb->PHYSET = physet;
|
||||
#endif
|
||||
R_BSP_SoftwareDelay((uint32_t) 1, BSP_DELAY_UNITS_MICROSECONDS);
|
||||
physet &= (uint16_t) ~RUSB2_PHYSET_DIRPD_Msk;
|
||||
rusb->PHYSET = physet;
|
||||
R_BSP_SoftwareDelay((uint32_t) 1, BSP_DELAY_UNITS_MILLISECONDS);
|
||||
rusb->PHYSET_b.PLLRESET = 0;
|
||||
|
||||
// set UTMI to operating mode and wait for PLL lock confirmation
|
||||
rusb->LPSTS_b.SUSPENDM = 1;
|
||||
while (!rusb->PLLSTA_b.PLLLOCK) {}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -376,7 +376,7 @@
|
||||
//------------ RUSB2 --------------//
|
||||
#if defined(TUP_USBIP_RUSB2)
|
||||
#define CFG_TUD_EDPT_DEDICATED_HWFIFO 1
|
||||
#define CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE (2 | (TUD_OPT_HIGH_SPEED ? 4 : 0)) // 16 bit and 32 bit if highspeed
|
||||
#define CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE (2 | 4) // HS module uses 32-bit access at any link speed (e.g. FS-forced build)
|
||||
#define CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE 0
|
||||
#define CFG_TUSB_FIFO_HWFIFO_CUSTOM_WRITE // custom write since rusb2 can change access width 32 -> 16 and can write
|
||||
// odd byte with byte access
|
||||
|
||||
Reference in New Issue
Block a user