| Age | Commit message (Collapse) | Author |
|
PVS-Studio (V530) flagged the discarded return value of usbss_phy_cfg at every
call site. The PHY back-door read-back is only a strobe; the value is never used
(matching the WCH EVT demo), so return void. PVS found no other issues in either
CH32H417 dcd.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01QWhrtHjbSVMKcY68vxBwBm
|
|
Apply correctness-review findings on the USBSS SuperSpeed dcd (still pre-hardware):
- Critical: the LINK DISABLE fallback path left TIM12 running, so once the driver
switched to USB2 (where the ISR routes to the USB2 handler and never clears the
timer flag) the update flag stormed the CPU. Both fallback entry points now go
through fallback_to_usb2(), which stops TIM12 first.
- Critical: dcd_set_address in fallback mode called ch32h417_usb2_edpt0_status_complete
with a NULL request (dereferenced immediately) and fell through into the USB3 path.
It now arms the USB2 status ZLP and returns; the address is applied by the USB2
status-complete callback usbd invokes with the real request.
- Major: the control STATUS stage (USBSS_UDIF_STATUS) never completed the queued
zero-length status transfer, so usbd's status-stage callback never ran and EP0
stayed busy. The status ZLP does not raise a UIF_TRANSFER on this controller, so it
is now completed in the UDIF_STATUS branch (and handle_ep0_in no longer completes a
zero-length IN early).
- Also: LINK_STATE_INACTIVE now counts toward the fallback attempt limit (matches the
file header and the vendor), the LINK_CFG init uses LINK_TX_DEEMPH_MASK exactly like
the WCH EVT literal, and a dead if/else in the EP0-OUT arm path is collapsed.
Build-verified: both full example matrices (SPEED=super and =high) build 20/20.
Remaining review notes (U1/U2/U3 PHY low-power reconfig, RECOVERY case, SET_ISOCH_
DELAY -> LINK_ISO_DLY, OUT-DMA clamp) are deferred to hardware bring-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01QWhrtHjbSVMKcY68vxBwBm
|
|
Apply correctness-review findings on the USB2 HS dcd (still pre-hardware):
- EP0 control OUT arming now writes the full UEP0_RX_CTRL byte (DATA1|ACK) instead
of a read-modify-write, so RB_UEP_R_SETUP_IS is cleared - otherwise the data/
status OUT following a SETUP would be re-detected as a new SETUP and every control
transfer after the first would break. The SETUP handler likewise writes full
TX/RX bytes. This also fixes the missing DATA1 expected-toggle on a control-write
data stage.
- Add the SOF interrupt branch (USBHS_UDIF_RX_SOF is a top-level INT_FG bit on this
IP, not an INT_ST token): dcd_sof_enable() armed SOF but the ISR dropped it, so
dcd_event_sof() was never delivered.
- Drop the extra INT_FG=USBHS_UDIF_TRANSFER write: the vendor acknowledges a
transfer solely by clearing the per-endpoint DONE bit; the aggregate write could
clear a second endpoint's still-pending completion (only one is serviced per IRQ).
Build-verified (SPEED=high links). Hardware validation still pending.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01QWhrtHjbSVMKcY68vxBwBm
|
|
Second stage: the USBSS SuperSpeed device driver (SPEED=super now links). The
LINK layer is the same IP as the CH569 but the endpoint engine is the H417's
reworked chain-DMA design (hardware SEQ_AUTO/ERDY_AUTO, persistent RB_EP_*_HALT,
per-chain completion). The RCC/PHY/LINK bring-up and EP0 control sequences are
transcribed from the WCH EVT USBSS device demo; data endpoints arm one chain per
packet (burst 1) for correctness-first bring-up, matching the CH569 port's
initial path - raise CFG_TUD_WCH_USB30_MAX_BURST once the hardware validates.
Buffers may live anywhere in the shared SRAM (all DMA-reachable), so no bounce
machinery is needed.
- src/portable/wch/dcd_ch32h417_usb30.c (+ ch32h417_usb30_reg.h): full dcd with
a STATE_CHG-driven LTSSM handler, LMP PORT_CAP/PORT_CFG exchange, EP0 DPH/ERDY
flow with software sequence, chain-armed data endpoints, per-EP HALT stall, and
a TIM12-backstopped runtime USB2 fallback that hands rhport 0 to ch32h417_usb2_*
when SuperSpeed training fails (host has no SS port).
- examples/device/*/skip.txt: add mcu:CH32H417 to the FreeRTOS + audio/video
examples (no FreeRTOSConfig for this family yet), mirroring mcu:CH569.
Build-verified: all 20 device examples build for SPEED=super, and cdc_msc for
SPEED=high, clean under -Werror (riscv-none-elf-gcc 13.2). Hardware bring-up at
5 Gbps (usbtest battery, throughput) is pending rig availability.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01QWhrtHjbSVMKcY68vxBwBm
|
|
First stage of the CH32H417/CH32H416 SuperSpeed port (the second WCH SuperSpeed
device family after CH569). This lands the family scaffolding and the USB2.0
high-speed device driver; the USB3 dcd is a stub for now (SPEED=super does not
link yet).
- tools/get_deps.py, src/tusb_option.h (OPT_MCU_CH32H417 2260 + CH32H416 alias):
openwch/ch32h417 SDK dependency.
- src/common/tusb_mcu.h: OPT_MCU_CH32H417 block with distinct USB IP tokens
(TUP_USBIP_WCH_USB30_H417 / _USBHS_H417) so neither the CH569 nor the CH32V307
drivers match; SPEED=super|high selectors mirror the CH569 port; EP0 = 512 on SS.
- hw/bsp/ch32h417: family runs entirely on the V3F core (core 0, boot core), single
image in the 512 KB shared SRAM (the only SRAM on the bus matrix, USB-DMA
reachable). family.c aliases USBSS/USBSS_LINK/USBHS(/TIM12) to one forwarder
(the CH569 LTO identical-code-folding hazard applies), SysTick0 tick, USART1
PA9 debug, ESIG unique id. Linker derived from the EVT V3F script. Board
nanoCH32H417 (MuseLab): green LED PC2 active-low, WCH-LinkE VCP on USART1.
- src/portable/wch/dcd_ch32h417_usbhs.c (+ ch32h417_usbhs_reg.h, dcd_ch32h417.h):
USB2.0 HS dcd for the H417 USBHS IP (per-EP INT_ST EP_DIR+EP_ID, SETUP_IS flag,
DONE flags, split UEP_TX/RX_EN, NAK/STALL/ACK encoding, manual toggle). Core
exposed as ch32h417_usb2_* internals for the upcoming USB3 runtime fallback;
no RAMX bounce (all shared SRAM is DMA-capable, unlike CH56x).
Build-verified: board_test and cdc_msc SPEED=high link cleanly under the full
-Werror set (riscv-none-elf-gcc 13.2). Hardware bring-up pending (see next).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01QWhrtHjbSVMKcY68vxBwBm
|
|
The CH569 USBSS ctrl_out flakiness (cases 14/21, host -EPROTO) is a
silicon erratum, not a driver bug: EP0 OUT data stages whose wLength
% 4 == 1 are intermittently dropped at 5 Gbps. Proven by A/B against
WCH's own binary USB3 stack (SimulateCDC EVT demo patched to accept
the gadget-zero 0x5b/0x5c requests), which fails identically on the
same board — independent of host LPM (persists with U1/U2 disabled)
and sysclk (80 vs 120 MHz). Bulk OUT at the same lengths is clean.
Document the erratum in the dcd and drop the ineffective zeroed-BOS
workaround.
Rather than letting known-impossible cases flake the battery, the
firmware now advertises quirk flags in bcdDevice bits 4-7 (tier stays
in bits 0-3): 0x10 skips ctrl_out 14/21, 0x20 skips ep-halt 13 (the
previously proven single-STALL limitation). usbtest.py skips flagged
cases visibly at SuperSpeed only (explicit --tests still runs them)
and reports them in the summary/JSON.
Also fix the CH56x USB3 dcd leaving an endpoint halted across
reconfiguration: clear the stalled flag in dcd_edpt_open/close/
close_all (USB 2.0 9.4.5) so a SET_CONFIGURATION recovery of a
halted-then-reopened endpoint does not STALL forever.
Validated on hydrausb3_v1 at 5000M: tier-4 battery x3 = 27/27 passed
+ 3 skipped, full HIL device suite 12/12 green (MSC 160/134 MB/s).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
Improvements from chasing the intermittent usbtest ctrl_out failure
(cases 14/21) at SuperSpeed:
- Process a latched status-stage completion before a simultaneously
latched SETUP: with back-to-back control transfers both flags can
arrive in one ISR pass and taking the SETUP first dropped the
previous transfer's completion inside usbd
- Status completion is recognized from either event side via a shared
helper (_ep0_status_pending), and the status arm keeps both
directions armed - hardware-verified that the RX engine must
acknowledge the STATUS TP even for an IN status (RX unarmed makes
SET_ADDRESS time out)
- EP0 idles ACK-armed and an OUT data packet that lands before usbd
arms the stage is held (_ep0_early_len) and delivered at arm time;
the ERDY for a host-to-device data stage is signalled from the SETUP
ISR (matching the reference flow) instead of only ~100 us later from
task context
- EP0-OUT data arms OR into RX_CTRL, preserving the live packet
sequence bits like the reference's USB30_OUT_Set
Known issue (documented for future work): ctrl_out remains flaky at
SuperSpeed with a ~0.3% per-transfer failure rate (EPROTO on the host).
Exhaustively characterized on hardware: the device receives the SETUP
exactly once and arms correctly (post-arm RX_CTRL/DMA/LINK_STATUS
identical to passing iterations, verified by ISR-time tracing), then
the host's data DP never arrives; bulk OUT at identical sizes is
error-free, host LPM off makes no difference, and neither NRDY holds,
double ERDY, sequence preservation nor removing the iso endpoints
change the rate. Root-causing needs a USB3 protocol analyzer or WCH
silicon documentation. High speed is unaffected (30/30).
Battery results with this commit: 30/30 high speed, 29/30 SuperSpeed
(case 13 halt = documented silicon limitation, cases 14/21 pass most
rounds).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
The register model comes from disassembling WCH's official USB30 device
blob (libCH56x_USB30_device_lib.a): USB30_ISO_Setendp reveals per-
endpoint isochronous mode bits in UEP_CFG (bit 16+ep for OUT, bit 24+ep
for IN); arming and completion then use the normal endpoint flow. The
usbtest example moves back to tier 4 at SuperSpeed with an iso pair in
the SS descriptor set - all four kernel usbtest iso cases (15/16/22/23)
pass on hardware at 5000M.
The endpoint-halt latch from the previous commit is kept (it fixes
usbtest case 29); case 13 is now documented as a silicon limitation
after exhaustive on-hardware experiments: a halted endpoint answers
exactly one probe with a STALL TP and no re-arm strategy (any NUMP,
ERDY, 30 us timer re-assertion, endpoint enable bounce) revives it -
no event fires per stall TP. The CH32H417's reworked endpoint engine
adds a persistent RB_EP_TX_HALT mode, evidently addressing exactly
this. Battery results: 30/30 at high speed, 28/30 at SuperSpeed
(case 13 = silicon halt limitation; case 14 ctrl_out is intermittent
- pre-existing EP0 OUT flakiness under investigation).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
Running the Linux kernel usbtest/testusb battery against the CH569 for
the first time. Results: 30/30 at high speed (4 consecutive reflash
cycles), 25/26 at SuperSpeed.
DCD fixes found by the battery:
- dcd_ch56x_usbhs: clear-halt on an armed bulk-OUT pipe reset the toggle
but dropped the queued receive, NAKing forever (usbtest case 29 - the
same defect previously found on rp2040/fsdev/ch32_usbhs/rusb2)
- dcd_ch56x_usb30: endpoint halt is now latched; the endpoint event
handler used to clear the response field on every completion, wiping a
just-set STALL while the peer streamed, and clear-halt now re-arms a
still-armed transfer with the fresh packet sequence (cases 13/29)
usbtest example gains SuperSpeed support: tier 3 on SS builds, a
4-endpoint SS descriptor set with companions, EP0 512 config and
1024-byte receive buffers.
test/hil/usbtest.py gains SuperSpeed-aware case parameters: bulk sizes
and vary steps must be 1024-multiples at SS or the device's whole-packet
source overruns the host request (case 4 EOVERFLOW).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
Brings the usbtest device example (gadget-zero source/sink peer for the
kernel's usbtest.ko/testusb), the test/hil/usbtest.py battery runner and
the extended vendor class driver (interrupt/isochronous endpoints, alt
settings) into the CH569 SuperSpeed branch, enabling the battery to run
against both CH56x dcds.
Conflict resolution: test/hil/tinyusb.json keeps this branch's
hydrausb3_v1 fixture entry and adds the usbtest branch's
ch32v307v_r1_1v0 entry.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
Measurements with the 9*MSS window showed iperf pinned at 51 Mbit/s with
the in-flight data exactly tracking the advertised window: the 120 MHz
core's TCP receive path is the bottleneck, not the window or the USB
link. Recover the software-checksum cost: inbound IP/UDP/TCP checksum
VERIFICATION is skipped on the SuperSpeed build (USB already CRC32
protects every payload; outbound generation stays on). iperf over
SuperSpeed improves 51 -> 74.5 Mbit/s (device RX, lwiperf).
Also from bringing this up on a marginal SS lane that takes seconds to
train (normal lanes train in <100 ms):
- CFG_TUD_WCH_USB30_FALLBACK is now overridable from the build line;
=0 builds SuperSpeed-only, which retries training indefinitely
instead of settling for high speed
- the fallback keeps retrying twice as long (4.4 s) when far-end RX
terminations were seen: terminations mean an SS-capable partner
exists, while a USB2-only host (no terminations) still falls back
after ~2 s
Validated on HydraUSB3 behind a Renesas uPD720201 hub; 65/65 unit
tests, full example sweep, pre-commit clean.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
iperf on the SuperSpeed build is TCP-window-bound (51 Mbit/s with the
6*MSS window). Reclaim RAMX to widen it:
- dcd_ch56x_usbhs: new CFG_TUD_WCH_USBHS_EP_MAX limits usable endpoint
numbers and scales the static RAMX bounce buffers accordingly
(default unchanged at 8 endpoints)
- net_lwip_webserver: limit the (fallback) USB2 endpoints to 1-3,
freeing 4 KB of RAMX; grow the SuperSpeed build to PBUF_POOL_SIZE 9 /
TCP_WND 9*MSS (the high-speed build keeps 6/6). RAMX 94% of 32 KB.
High-speed fallback verified unchanged on HydraUSB3 (33 Mbit/s, full
enumeration); the SuperSpeed window measurement is pending the rig's SS
lane, which trains only intermittently (analog-marginal path - same
binaries trained and ran 51 Mbit/s earlier the same day).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
Two fixes found bringing up SuperSpeed on a Renesas uPD720201-based hub:
- dcd_ch56x_usb30: retry link training with a fresh detect cycle every
second fallback-timer tick (4 ticks total) before switching to the
USB2 fallback. Some hubs only complete Polling on a re-attempt; with
a single continuous attempt the link reached U0 rarely and the port
fell back to high speed almost every boot.
- net_lwip_webserver: CFG_TUD_ENDPOINT0_SIZE was left at plain 64, so
usbd chunked EP0 data at 64 bytes; on a 512-mps SuperSpeed control
endpoint the first chunk is a short packet, the host ends the data
stage early and the status stage deadlocks until timeout ("can't
read configurations, error -110"). Use the SuperSpeed-aware size and
clamp the device-qualifier bMaxPacketSize0 like the other examples.
Validated on HydraUSB3: NCM enumerates at 5000M reliably across warm
reboots; lwiperf over SuperSpeed measures 51 Mbit/s device RX (TCP
window bound; 33 Mbit/s on the high-speed fallback), MSC 169/136 MB/s.
Full example sweep, 65/65 unit tests, pre-commit clean.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
Replace the hydrausb3/wch-ch56x-bsp dependency with WCH's official EVT
(openwch/ch569 @0424d2968); rename the family hw/bsp/ch56x -> hw/bsp/ch569
to match. Adaptations for the EVT layout and API:
- family.cmake/mk: EVT/EXAM/SRC paths (Peripheral/src|inc, RVMSIS, Startup)
- CH56xSFR.H case-wrapper (EVT includes the uppercase name, which breaks
case-sensitive filesystems)
- debug_uart: official UART1_BaudRateCfg + explicit TXD1/RXD1 pin setup
(the EVT has no pin-configuring UART init)
- family.c: .dmadata (RAMX) zeroing moved into board_init (EVT startup
only clears .bss) and a self-contained SPI-ROM reader for the factory
unique ID (the EVT flash API is a binary blob, not linked)
- dcd_ch56x_usbhs.c: plain volatile casts instead of hydrausb3-only
pointer typedefs
- -Wno-comment for the EVT's nested header comments
Validated: full 19-example CMake sweep + Make build green, 65/65 unit
tests, pre-commit clean; on HydraUSB3 hardware cdc_msc_throughput
enumerates at SuperSpeed 5000M with MSC 124/110 MB/s read/write
(direct-IO dd).
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019MRGjBT2NBkCoWwyDT4LaE
|
|
- TUD_CDC_NCM_SS_DESCRIPTOR template (bulk 1024 + endpoint companions)
and companion handling in netd_open (notification endpoint skip,
bulk pair drv_len)
- net_lwip_webserver: SS device/config/BOS descriptors for the NCM
build, CH569 added to the LWIP_HIGH_THROUGHPUT tier (builds lwiperf).
The 16 KB RAMS cannot hold lwIP: heap and pools go to the 32 KB RAMX
via LWIP_DECLARE_MEMORY_ALIGNED (arch/cc.h), with a 6-pbuf pool /
6*MSS window and a 3 KB dcd bounce pool to fit (RAMX 92%)
- CH569 dcd fixes found while bringing this up: TMR0 counts only 26
bits, so the previous 1 s fallback CNT_END (120e6 > 2^26) silently
never expired - use 0.55 s per phase; make link busy-waits in
usb30_hw_init best-effort so a stuck-BUSY teardown state cannot
abort re-initialization half way
Builds clean (full ch56x example sweep); NOT yet hardware-validated:
the rig's WCH-LinkE stopped completing flash writes mid-session (reads
and erases fine), leaving the board without firmware - iperf numbers
pending physical probe/board recovery.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
- Default CFG_TUD_WCH_USB30_MAX_BURST to 4 (tusb_mcu.h): matches the
link layer's header-packet buffer count (NUM_HP_BUF). Burst 8 fails
to configure; the TeenyUSB-reported burst corruption quirk did not
reproduce with per-burst DMA re-arm (50x MSC write/read-back and
256KB CDC echo integrity all clean at burst 4)
- Bounce slots stay one max packet regardless of burst (bounced
transfers cap their burst to the slot; zero-copy uses the full burst)
so the RAMX pool no longer scales with burst size
- cdc_msc/cdc_msc_throughput SS descriptors advertise
bMaxBurst = CFG_TUD_WCH_USB30_MAX_BURST-1 via the
CFG_EXAMPLE_SS_BULK_MAXBURST knob (0 for non-WCH SuperSpeed ports)
Measured on HydraUSB3 v1 at 5 Gbps (HIL cdc_msc_throughput): MSC read
147 MBps write 127 MBps - about 2x the single-packet figures (raw dd:
up to 163 MB/s read at 1M blocks).
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
- SS device/config/BOS descriptors with endpoint companions, speed-aware
EP0/CDC/MSC buffer sizes (same pattern as cdc_msc)
- Lengthen the CH569 SuperSpeed-training fallback timeout to 1 s per
phase: training from a host port settled in another connection state
can exceed the previous 0.5 s window
Measured on HydraUSB3 v1 at 5 Gbps (NUMP=1): MSC sequential read
70-79 MB/s, write 55 MB/s via dd direct IO - about 3.5x the USB2
high-speed fallback figures on the same setup. Burst support
(CFG_TUD_WCH_USB30_MAX_BURST > 1) remains the main headroom.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
- CFG_TUD_WCH_USB30_FALLBACK (on by default for SPEED=super): the USB3
dcd owns both controllers on rhport 0. TMR0 times out SuperSpeed link
training (2x 0.5 s: shut USB3 down, then bring USB2 up); an LTSSM
disabled event (SS rejected by the port) switches immediately. The
active controller is dispatched at each dcd entry point; IRQ pendings
are cleared on switch and dcd_int_enable is mode-aware (a stale LINK
pending otherwise refires on every usbd queue unlock)
- Bounded link busy-waits (LINK_STATUS busy can stick once the link is
torn down)
- board_get_unique_id from the factory ID in read-only info flash
(0x77FE4), so the USB serial identifies the chip (HIL requirement)
- HIL: replace ch582m_evt (probe moved) with hydrausb3_v1
- Regenerate boards.rst, dependencies.rst, BoardPresets.json
Verified on HydraUSB3 v1 (ci rig): SuperSpeed still trains at 5000M
with fallback armed (3x replug); with SS terminations disabled the
device falls back and enumerates fully at 480M. Full HIL run passes
10/10 device tests including cdc_msc at SuperSpeed and
cdc_msc_throughput (MSC 22.1/14.1 MBps read/write at NUMP=1);
non-SuperSpeed-aware examples recover onto USB2 via the fallback.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
New dcd_ch56x_usb30.c + ch56x_usb30_reg.h (both MIT, written fresh):
- Register model from the hydrausb3 project's reverse engineering
(wch-ch56x-lib, Apache-2.0) and TeenyUSB's CH56x port (MIT), with
LINK-layer bit names per the officially documented CH32H417 (same IP,
CH32H417RM chapter 27); TU_VERIFY_STATIC offset asserts guard the
vendor header layout
- Software-assisted LTSSM in the LINK interrupt: polling/TXEQ/ready
sequencing, LMP PORT_CAPABILITY + PORT_CONFIGURATION exchange, warm
and hot reset handling. Link-up (ready after TXEQ) is reported as
dcd_event_bus_reset with TUSB_SPEED_SUPER
- EP0 with fixed 512-byte mps: SETUP via RX flag bit30, unified status
stage via bit29 (SET_ADDRESS applied there), manual IN packet sequence
- Data endpoints with per-burst DMA re-arm, partial burst-drain
accounting, ERDY flow control and NUMP=1 default (multi-packet burst
silicon quirk); RAMX bounce slots from a small pool for transfer
buffers outside RAMX; isochronous not supported
- Board vector forwarders unified into one handler + symbol aliases:
gcc identical-code-folding otherwise rewrites one interrupt handler
as a call into another whose mret skips the caller's epilogue,
leaking a stack frame per interrupt (found the hard way)
- hydrausb3_v1 default SPEED flipped to super; skip net_lwip_webserver
(16 KB RAMS, same as other small-RAM parts)
Verified on HydraUSB3 v1 hardware (ci rig, USB3 host port):
- cdc_msc enumerates at 5000M: bcdUSB 3.20, EP0 mps 2^9, endpoint
companion descriptors, BOS with USB2-ext + SuperSpeed capability
- CDC echo and MSC FAT12 reads functional at SuperSpeed; 30x sustained
read stress and 5x replug/reset cycles all re-enumerate at 5000M
- SPEED=high regression: still enumerates at 480M
- Full example sweep builds for both controllers
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
- OPT_MCU_CH569 (2250) + tusb_mcu.h capability block: USB3 SuperSpeed
(USBSS) or USB2 HighSpeed (USBHS) selected at compile time via
CFG_TUD_WCH_USBIP_USB30/USBHS, both on rhport 0
- New dcd_ch56x_usbhs.c: CH56x USB2 HS device driver ported from
dcd_ch32_usbhs.c (same bit encoding, CH56x-specific register layout:
R8_UEPn_MOD enables, different DMA/MAX_LEN/control offsets, 8 EPs).
Internals exposed as ch56x_usb2_* (dcd_ch56x.h) for the upcoming USB3
dcd's runtime fallback. USB DMA only reaches RAMX: buffers in the
.dmadata section, non-RAMX transfer buffers bounce per-endpoint
- hw/bsp/ch56x family (SDK dep hydrausb3/wch-ch56x-bsp, Apache-2.0, no
binary blobs) + hydrausb3_v1 board: 120 MHz clock (required for USB3),
UART1 PA8/PA7 logging, LED PB22, button PB23, linker script with
16-byte aligned NOLOAD .dmadata section in RAMX
- SPEED=high|super build option (default high until the USB3 dcd lands)
- CI: add ch56x to riscv-gcc matrix; skip FreeRTOS/audio/video examples
(as CH583) and msc_dual_lun (16 KB RAMS, as SAMD11/MKL25)
Verified: cdc_msc builds via CMake and Make; full example sweep green;
.dmadata lands at 0x20020000 (RAMX) with 16-byte alignment.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
|
# Conflicts:
# lib/rt-thread/port/msc_device_port.c
# src/device/usbd.c
|
|
The CH32V307 exposes two device controllers: the full-speed USBFS (dcd_ch32_usbfs,
selected with -DSPEED=full) and the high-speed USBHS (dcd_ch32_usbhs, the default).
Both now pass the Linux testusb tier-4 battery 30/30 (3 reflash cycles each) on
ch32v307v_r1_1v0.
- dcd(ch32_usbhs): fix clear-stall on an armed OUT endpoint. It set the pipe to
NAK unconditionally, so a receive the class still considers submitted NAKed
forever after CLEAR_FEATURE(HALT) and usbtest toggle test 29 timed out. Re-queue
the pending receive (ACK/NYET) instead. This is the first usbtest coverage of the
ch32_usbhs driver.
- bsp(ch32v30x): implement board_get_unique_id from the ESIG UID (0x1FFFF7E8);
the port previously fell back to the placeholder serial.
- usbtest: the CH32V307 usbfs port caps iso at 64 B like the other WCH USBFS parts,
so add it to the FS iso-64 descriptor branch (its usbhs port uses the HS sizes).
- test/hil: register ch32v307v_r1_1v0 (usbhs + usbfs variants, WCH-Link BC5DA47360D0)
and move nrf54lm20dk to boards-skip.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Cross-checking the branch against the MCU datasheets surfaced a regression and
several latent defects. Fixes (finding # from the review):
#1 dcd_ch32_usbfs: restore EP3's enlarged iso buffer. CH32V20x/V30x/F20x USBFS
give endpoint 3 a 1023-byte iso packet (CH32FV2x_V3xRM ch23; 16-bit
R16_UEP3_T_LEN), unlike every other 64 B endpoint. Commit c05e9313e removed
that buffer and capped iso at 64 B, breaking >64 B FS iso (UVC/UAC) on EP3.
Re-add it via CFG_TUD_WCH_USBFS_EP3_BUFSIZE, defaulted to 1023 on those parts
(CH32_USBFS_EP3_1023_BUFSIZE in ch32_usbfs_reg.h) and 64 elsewhere.
#3 dcd_ch32_usbfs: make data.isochronous[] per-direction and clear it on
endpoint (re)open, so a reused ep number can't inherit a stale iso response.
#7 dcd_ch32_usbfs: exempt iso from the PID_OUT data-toggle handling too (was
only done for PID_IN); iso is DATA0-only in both directions.
#13 dcd_ch32_usbfs: bounds-assert ep < EP_MAX in dcd_edpt_iso_alloc.
#2/#6 usbtest descriptors: TUD_OPT_HIGH_SPEED is a compile-time capability, not
the live speed, so the full-speed config (and OTHER_SPEED) advertised int/iso
mps 512 on HS boards — illegal at FS / over the 1023 B/frame iso budget. Split
into FS-legal / HS descriptor sizes; the plain macro tracks operating speed.
#10 vendor: non-alt free-slot detection also checks the int endpoints (a vendor
interface may now be interrupt-only, no bulk ep).
#14 vendor: replace the order-coupled ITF_MEM_RESET_SIZE ladder (2 unreachable
branches) with offsetof(vendord_interface_t, tx_stream).
#5 rp2040: clear_stall re-issue preserves the already-transferred count so a
mid-transfer clear-halt reports the full length, not just the remainder.
#8 rp2040: force single-buffering for iso (only BULK gets a double DPRAM buffer),
so a multi-packet iso xfer can't spill buffer 1 into the next ep's DPRAM.
Verified: CH32V203 usbfs and CH583 still pass usbtest tier-4 30/30; all board
families build; pre-commit (format/codespell/unit) clean.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
The CH582/CH583 USBFS DCD refused isochronous outright, but the datasheet
(CH582-CH583 DS1, USB controller chapter) lists synchronous/real-time
(isochronous) alongside control/bulk/interrupt, with 64 B packets. Accept iso
like the other WCH USBFS parts (iso mps 64, capped to the 64 B endpoint buffer;
drop the CH583-only refusals in dcd_edpt_iso_alloc/activate).
That alone left iso IN dropping a deterministic ~1/8 of packets. Root cause:
CH58x drives the data toggle manually in the ISR (its hardware AUTO_TOG is
unreliable) and flipped the TX toggle after every IN packet — including
isochronous, which is DATA0-only and must never toggle. Skip the manual toggle
flip for iso endpoints. This path is CH58x-only (CH32_USBFS_EP_MANUAL_TOG),
so CH32V103/V20x/V307 (hardware AUTO_TOG) are unaffected.
Verified on ch582m_evt: Linux testusb tier-4 (30 cases) = 30/30 across 4 reflash
cycles; CH32V103 and the CH32V203 usbfs port still pass. Drop CH583 from the
usbtest skip list — all WCH USBFS parts now run the full battery.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
CH32V103 (ch32v103r) uses the same usbfs DCD as the CH32V203 usbfs port and
passes tier-4 30/30 with the fixes already in place (iso mps 64 + the clear-halt
re-arm) — drop it from the skip list. Verified 30/30 across 6 reflash cycles.
dcd_ch32_usbfs.c gave EP3 an enlarged 1023-byte IN buffer for full-speed iso,
but the CH32V20x/V103 USB FS controller caps every endpoint (bulk/interrupt/iso)
at a 64-byte packet (per the RM "USB Full-speed Host/Device Controller"), so no
endpoint can ever send more than 64 B and the oversized EP3 buffer was pure
waste. Treat EP3 like any other endpoint (64 B OUT + 64 B IN) and drop the
special buffer, saving ~1 KB of RAM. dcd_edpt_iso_alloc now rejects an iso mps
> 64 rather than running off the end of the buffer. usbtest (iso mps 64) still
passes 30/30 on CH32V103 and the CH32V203 usbfs port.
CH583 has no isochronous support in its USBFS IP, so it cannot run tier-4 (the
iso endpoints fail to open and SET_CONFIG times out); it stays skipped.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
Neither of the CH32V203's two USB device IPs could run the usbtest tier-4 battery.
usbfs (port 1):
- 128 B iso doesn't fit its fixed 64 B/ep buffers -> drop iso mps to 64 on CH32
(usb_descriptors.h).
- dcd_ch32_usbfs.c: clear-halt on an armed OUT endpoint forced it to NAK, so a
submitted read NAK'd forever and the host timed out. Re-assert ACK when a
receive is still armed (usbtest toggle test 29).
fsdev (port 0, the shared stm32_fsdev driver):
- The descriptor hard-coded the interrupt mps, so USBTEST_INT_EP_MPS never took
effect; all six tier-4 endpoints then overflowed the 512 B PMA and SET_CONFIG
timed out. Wire the interrupt mps to the macro and use 16 on the fsdev port.
- The USBD raises three IRQ lines (LP/HP/WakeUp) into one non-reentrant handler
and they nest (HP preempts LP) with QingKe HWSTK enabled. The mainline
toolchain's plain __attribute__((interrupt)) emits a software prologue that
fights the hardware context stack and corrupts the return on nesting -> wild
jump / illegal-instruction crash. Emit naked handlers that rely on HWSTK for
context save/restore -- the equivalent of WCH's "WCH-Interrupt-fast", which
mainline riscv-none-elf-gcc ignores -- so nesting is safe.
- Single-buffered iso IN can't stay fed under load; double-buffer it
(CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP in the example tusb_config.h). iso mps
32 x2 = 64 B/ep keeps the same PMA budget. Left single for other fsdev parts
(audio's larger iso would overflow their PMA too).
Verified on nanoch32v203 (CH32V203D6): Linux testusb tier-4 (30 cases across
bulk/control/interrupt/isochronous) = 30/30 on both the fsdev and usbfs variant,
each across 5 reflash cycles. Full CH32V20X example set builds; cdc_msc still
enumerates CDC+MSC on the fsdev port (naked-handler regression check). Drop
CH32V20X from the usbtest skip list.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HeF2gZ1M7GWkz6Av4BpKPg
|
|
* license: use SPDX identifiers for src/ headers
Replace the full ~20-line MIT license boilerplate on every src/ file with a
two-line SPDX tag (SPDX-FileCopyrightText + SPDX-License-Identifier), following
the REUSE convention used by CircuitPython and the Linux kernel. Removes ~3500
lines of duplicated boilerplate.
|
|
The BSP family and MCU option were named "ch58x"/"CH58X", but the supported part is
the CH583/CH582 (and the SDK repo is openwch/ch583); CH585 is a separate MCU family,
so the CH58x umbrella was misleading. Rename to the specific family:
- hw/bsp/ch58x -> hw/bsp/ch583 (dir), and the BSP-local files ch58x_it.* ->
ch583_it.*, system_ch58x.* -> system_ch583.* (include guards/refs updated). The
vendor SDK files (CH58x_common.h, CH58x_*.c in hw/mcu/wch/ch583) keep their names.
- OPT_MCU_CH58X -> OPT_MCU_CH583 in tusb_option.h, tusb_mcu.h, and the shared WCH
USBFS driver (ch32_usbfs_reg.h, dcd_ch32_usbfs.c). OPT_MCU_CH582 is kept as an
alias (same value), so either name selects the same code.
- FAMILY_MCUS CH58X -> CH583, CFG_TUSB_MCU=OPT_MCU_CH583, mcu:CH58X -> mcu:CH583 in
the example skip lists, the CI build matrix (ci_set_matrix.py), the get_deps family
tag, and docs/reference/boards.rst.
Board names (ch582m_evt, yd-ch582m) are unchanged. Verified: make + cmake build for
ch582m_evt, and ci.lan HIL (all device examples pass).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The OUT data-toggle check -- drop a packet whose DATA0/DATA1 doesn't match the
expected toggle (a host retransmit after a lost ACK, or a host that doesn't
alternate the toggle) -- only ran on CH58x. The auto-toggle parts (V103/V20x/V307/
X035) never checked it, so a duplicate/retransmitted OUT was processed twice.
HiFiPhile confirmed it: a host patched to send DATA0-only had CH32V305 accept every
packet. Move the TOG_OK gate out of the CH58x-only block so it runs on every variant;
the manual toggle flip stays CH58x-only. EP0 keeps its own toggle via the SETUP/status
flow and is exempt.
Verified on ci.lan HIL: ch582m_evt (CH58x), ch32v103r_r1_1v0 (V103), nanoch32v203
(V203) all pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
dcd_edpt0_status_complete() wrote the full SET_ADDRESS wValue into R8_USB_DEV_AD,
clobbering bit 7, which on CH58x is a user general-purpose flag (only bits [6:0]
are the device address). Mask to 7 bits and preserve bit 7, matching the removed
dcd_ch58x_usbfs.c. CH58x-scoped; other parts keep the full write.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The PID_SETUP handler armed the new control transfer but left any in-flight EP0
transfer from the previous request marked valid, so a spurious EP0 IN/OUT could run
update_in()/update_out() against stale state (the removed dcd_ch58x_usbfs.c invalidated
both EP0 directions on every SETUP). Clear xfer[0] IN/OUT validity when a SETUP arrives.
Applies to all WCH USBFS parts -- a new SETUP always supersedes a pending control xfer.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The USBFS SUSPEND interrupt fires on both the suspend and the resume edge, but the
handler unconditionally posted DCD_EVENT_SUSPEND. On CH58x tud_resume_cb() therefore
never ran, and a device that lowered clocks/power in tud_suspend_cb() was never told
to restore them. Read MIS_ST's suspend bit (1 while suspended, 0 once resumed) to emit
DCD_EVENT_RESUME on the wake edge -- what the removed dcd_ch58x_usbfs.c did. Scoped to
CH58x via #if; the CH32 parts keep their existing behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
The manual-toggle ISR skipped EP0 entirely (if (ep != 0)), so EP0's RX data toggle
was set to DATA1 once at SETUP and never advanced. A control-OUT whose data stage
exceeds the EP0 packet size (a vendor/WebUSB OUT, a large HID SET_REPORT, or an
HS DFU download) desynced on the second packet and stalled.
EP0 has no hardware auto-toggle on CH58x (per the datasheet RB_UEP_AUTO_TOG applies
only to EP1/2/3/5/6/7), so flip its RX toggle on every OUT and always process the
packet -- restoring what the removed dcd_ch58x_usbfs.c did. The HIL examples keep
their control-OUT data stages within a single packet, so this was latent.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
dcd_edpt_xfer() re-enabled the USB interrupt before update_in() / ep_rx_set_response(),
which read-modify-write the (combined) EP control register. On CH58x the ISR RMWs that
same register to flip the manual data toggle, so a transfer interrupt landing mid-RMW
could drop the toggle flip and desync the endpoint. Move dcd_int_enable() to after the
arming so the whole sequence is atomic w.r.t. the ISR (matching the CH32X035 port #3703).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
Tidy the CH58x register/buffer layout the initial port left rough.
Register map (USBOTG_FS_TypeDef):
- Extend the struct to the full CH583/582 datasheet Table 17-2 map instead of
stopping at UEP567_MOD (0x0E) with the per-endpoint registers living only in
raw-address macros.
- Express the per-endpoint DMA/length/control registers as arrays of 4-byte
slots (ch58x_ep_dma_t / ch58x_ep_ctrl_t): EP0-3 DMA at 0x10, EP0-4 ctrl at
0x20, EP5-7 DMA/ctrl split to 0x54/0x64 (EP4 has no DMA register of its own;
it shares EP0's). TU_VERIFY_STATIC pins the slot sizes and block offsets, so
the EP_TX_LEN/EP_CTRL/EP_DMA macros walk each block by the 4-byte stride
(pointer arithmetic off slot 0, so the unused ternary branch can't trip
-Warray-bounds).
- Gate the two driver sites on CFG_TUSB_MCU == OPT_MCU_CH58X directly rather
than the CH32_USBFS_EP_REGS_CUSTOM alias, which was only ever defined in the
CH58x branch.
EP buffers (the data struct):
- Replace buffer[EP_MAX][2][64] on CH58x with named per-endpoint buffers: EP0/EP4
use the dedicated 192B ep0_ep4_buffer, so the old array left buffer[0]/buffer[4]
allocated-but-unused.
- Drop EP3's oversized iso buffer (out[64] + in[1023]); EP3 is bulk-only on CH58x,
so it uses a plain 128-byte buffer like the others. The data struct shrinks from
~2636 to 1292 bytes.
- Keep the now uniformly-64-byte buffers safe: dcd_edpt_iso_alloc()/iso_activate()
refuse isochronous on CH58x (no iso support; 8-bit T_LEN caps a packet at 255B),
and update_in()/update_out() additionally cap each packet copy to 64 bytes so a
class that ignores the iso-alloc result cannot run a memcpy past a buffer into a
neighbour's.
Non-CH58x parts (e.g. ch32v103) keep the struct-based macros, buffer[EP_MAX], and
the iso buffer unchanged. Verified on ch582m_evt HIL (ci.lan): all device examples
pass; ch32v103 build unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
Replace PR #3515's separate dcd_ch58x_usbfs.c / hcd_ch58x_usbfs.c with the
shared WCH USBFS device driver (combined per-endpoint control, like CH32V103),
adding two CH58x-specific behaviors guarded so CH32V103/V20x/V307 are unchanged:
- CH32_USBFS_EP_MANUAL_TOG: CH58x's hardware AUTO_TOG does not stay in sync, so
the ISR toggles DATA0/DATA1 manually and discards toggle-mismatched OUT
packets. Fixes multi-packet bulk-IN (e.g. MSC READ10) that otherwise hung.
- CH32_USBFS_EP4_SHARES_EP0: EP4 has no DMA register and overlays EP0's region
(EP0[0:63] + EP4 OUT[64:127] + EP4 IN[128:191]); add a 192-byte shared buffer
and buffer-pointer helpers (transparent for the other parts). Fixes
cdc_dual_ports (Port1 is on EP4).
Add the ch582m_evt board. Device only on USB0 (rhport 0): the shared
hcd_ch32_usbfs.c is CH32V20x-specific and cannot drive CH58x, so host / USB2
(rhport 1) is left commented out in the BSP for easy re-add.
Verified on ch582m_evt via local HIL: all device examples pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
|
|
CH32V103 uses the older USBFS IP: a single combined UEPn_CTRL register per
endpoint (IN response in bits [1:0], OUT response in [3:2], shared auto-toggle,
separate IN/OUT toggles) instead of the separate UEPn_TX_CTRL/UEPn_RX_CTRL
bytes of the newer IP (CH32V20x/V307). The shared driver was written for the
newer IP, so EP0 control transfers never worked on V103: the OUT response was
written to a reserved byte and the IN write clobbered the OUT bits.
- ch32_usbfs_reg.h: annotate the V103 register struct with byte offsets and add
a union exposing the combined UEPn_CTRL at the UEPn_TX_CTRL offset; define
CH32_USBFS_EP_CTRL_COMBINED and the combined-register bit positions.
- dcd_ch32_usbfs.c: abstract EP control access behind ep_tx/rx_ctrl_set() (full
write) and ep_tx/rx_set_response() (response-only RMW). The newer-IP path is
unchanged; the combined path read-modify-writes the single register and arms
the post-SETUP data stage at DATA1.
- bsp/ch32v10x: implement board_get_unique_id() (real chip UID) and drop the
CSR 0x800 writes that corrupted the QingKe V3 interrupt config and left all
interrupts disabled (the USB ISR never ran).
Verified on ch32v103r_r1_1v0: enumerates and passes HIL for cdc_msc, hid,
msc, midi, mtp, dfu, etc.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
|
Fix issue in the stall handling:
- dcd_edpt_stall() for an IN endpoint cleared EP_TX_LEN(0) instead of
EP_TX_LEN(ep_num), clobbering endpoint 0's transmit length register
when stalling any other IN endpoint.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Refactor the driver to follow USBFS style for easier maintenance.
Replace the old packet/response helpers with explicit queue and update paths for IN and OUT transfers.
Introduce transfer validity tracking and per-endpoint data toggle state.
Reset toggle state on init, close-all, endpoint close, clear-stall, and bus reset.
Initialize endpoint controls consistently in NAK + TOG_0 mode.
Tighten EP0 setup/status handling and route transfer IRQ processing through the transfer-flag path.
Stop enabling ISO_ACT in INT_EN and clear unhandled interrupt flags explicitly.
Signed-off-by: HiFiPhile <[email protected]>
|
|
It would casue race condition, SETUP packet is always acked.
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
OUT is still buggy
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
Signed-off-by: HiFiPhile <[email protected]>
|
|
EP_RX_CTRL state
|