Skip to content

CH32X035 port#3703

Open
rhgndf wants to merge 11 commits into
hathach:masterfrom
rhgndf:ch32x035
Open

CH32X035 port#3703
rhgndf wants to merge 11 commits into
hathach:masterfrom
rhgndf:ch32x035

Conversation

@rhgndf

@rhgndf rhgndf commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Adds support for both CH32X035 and CH32X033.

I'm also evaluating this MCU due to it being low cost and have USB.

CH32X035 has both TX and RX CTRL bits in the same byte, so there are changes to how these registers are written in the dcd.
If the current approach isn't satisfactory, there are some alternatives:

  • Make EP_TX_CTRL_WRITE a function instead of a #define
  • 'Inline' the #define: EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~MASK) | BITS

EP4 special handling:
This is a bit confusing, and Codex/Claude has been very helpful to get that part working. In short: no DMA and no auto-toggle, so some special handling has to be built in.

EP3 no special large buffer
The code has some existing handling for large EP3 buffer, but as far as I can tell that functionality doesn't exist in ch32x035, should I #if that out for this port?

Related: adafruit/Adafruit_TinyUSB_Arduino#486

@HiFiPhile

Copy link
Copy Markdown
Collaborator

CH32X035 has both TX and RX CTRL bits in the same byte, so there are changes to how these registers are written in the dcd.

Seems like v103, do you have board to test with ?

@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

Top 10 targets by memory change (%) (out of 2467 targets) View Project Dashboard →

target .text .rodata .data .bss total % diff
ch32v103c_bluepill/dfu_runtime 8,668 → 8,796 (+128) 9,036 → 9,164 (+128) +1.4%
ch32v103c_bluepill/hid_generic_inout 9,648 → 9,780 (+132) 10,016 → 10,148 (+132) +1.3%
ch32v103c_bluepill/hid_multiple_interface 10,484 → 10,616 (+132) 10,852 → 10,984 (+132) +1.2%
ch32v103c_bluepill/hid_boot_interface 10,504 → 10,632 (+128) 10,872 → 11,000 (+128) +1.2%
ch32v103c_bluepill/hid_composite 10,732 → 10,856 (+124) 11,100 → 11,224 (+124) +1.1%
ch32v103c_bluepill/midi_test 10,832 → 10,956 (+124) 11,200 → 11,324 (+124) +1.1%
ch32v103c_bluepill/cdc_dual_ports 11,848 → 11,976 (+128) 12,216 → 12,344 (+128) +1.0%
ch32v103c_bluepill/printer_to_cdc 12,016 → 12,144 (+128) 12,376 → 12,504 (+128) +1.0%
ch32v103c_bluepill/audio_test 12,124 → 12,252 (+128) 12,508 → 12,636 (+128) +1.0%
ch32v103c_bluepill/dfu 12,912 → 13,044 (+132) 13,376 → 13,508 (+132) +1.0%

@rhgndf

rhgndf commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Seems like v103, do you have board to test with ?

I don't have the board unfortunately. I may take a look if I need a 5V capable mcu though (although py32f07x is also 5V capable but has additional DACs unlike v103)

@rhgndf

rhgndf commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

MemBrowse Memory Report

Got claude to analyze the discrepency, here's the output verbatim:

  Why update_in got smaller

  The refactor folded the endpoint-buffer selection. Old code was a three-way branch:

  if (ep == 0)      memcpy(data.buffer[ep][TUSB_DIR_OUT], ...);  // buffer[ep][0]
  else if (ep == 3) memcpy(data.ep3_buffer.in, ...);
  else              memcpy(data.buffer[ep][TUSB_DIR_IN], ...);   // buffer[ep][1]

  New code is two-way, with the ep0 case absorbed into the inlined helper:

  if (ep == 3) memcpy(data.ep3_buffer.in, ...);
  else         memcpy(ep_buffer(ep, TUSB_DIR_IN), ...);   // buffer[ep][ep==0 ? 0 : 1]

  So the dedicated ep == 0 test-and-branch disappears; the address is now computed in a straight line as data.buffer[ep][ep==0 ? 0 : 1]. The disassembly confirms it: master has an extra li a5,3 / bne / beqz a4 cluster plus a second address computation
  that the branch version doesn't, and with RVC 2-byte instructions the savings land on exactly 8 bytes.

hathach added a commit to alt-0191/tinyusb that referenced this pull request Jun 19, 2026
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 hathach#3703).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hathach

hathach commented Jun 23, 2026

Copy link
Copy Markdown
Owner

thank you, after #3515, this should be relatively easy. I have the CH32X035 board but run out of wlinke debugger (attached to hil rig). will get this mcu ported when it got on my desk

@rhgndf

rhgndf commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

I also simplified the comments, since I found a hallucination involving X035.
Other changes:

  • refactored the ep buffer code for 58x and x035
  • removed ep3 in the non ep4 shared buffer case, saves 128 bytes of memory

@rhgndf

rhgndf commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

According to the manuals, it says only EP4 needs manual toggling, maybe manual toggle should only be enabled for EP4?

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Hardware-in-the-loop (HIL) Test Report

hfp.json

✅ 46 passed · ❌ 10 failed · ⚪ 0 skipped · blank not run

Board usbtest cdc_msc_throughput audio_test_freertos cdc_dual_ports cdc_msc cdc_msc_freertos dfu dfu_runtime hid_boot_interface hid_generic_inout midi_test msc_dual_lun mtp printer_to_cdc duration
stm32l412nucleo ✅ 30/30 ✅ C 637/386k M 852/815k 214s
stm32f746disco ✅ 30/30 ✅ C 13.6/9.7M M 15.4/30.5M 109s
stm32f746disco-DMA ✅ 30/30 162s
lpcxpresso43s67 ❌ 0/30 ✅ C 13.7/12.3M M 33.7/37.8M 255s

tinyusb-esp.json

✅ 12 passed · ❌ 12 failed · ⚪ 0 skipped · blank not run

Board usbtest msc_file_explorer_freertos audio_test_freertos cdc_msc_freertos device_info hid_composite_freertos duration
espressif_p4_function_ev ❌ 0/1 409 KB/s 59s
espressif_p4_function_ev-DMA ❌ 0/30 409 KB/s 59s
espressif_s3_devkitm ❌ 0/1 77s
espressif_s3_devkitm-DMA ❌ 0/30 166s

tinyusb.json

✅ 21 passed · ❌ 326 failed · ⚪ 17 skipped · blank not run

Board usbtest cdc_msc_throughput msc_file_explorer msc_file_explorer_freertos audio_test_freertos cdc_dual_ports cdc_msc cdc_msc_freertos cdc_msc_hid device_info dfu dfu_runtime hid_boot_interface hid_generic_inout host_info_to_device_cdc midi_test msc_dual_lun mtp printer_to_cdc duration
frdm_k64f 9s
ek_tm4c123gxl ❌ 0/30 143s
metro_m4_express ❌ 0/30 136s
raspberry_pi_pico_w 1106 KB/s 1016 KB/s 46s
feather_nrf52840_express ❌ 0/30 143s
raspberry_pi_pico ❌ 0/30 62 KB/s 219s
raspberry_pi_pico2 1110 KB/s 1022 KB/s 15s
max32666fthr ❌ 0/30 138s
adafruit_fruit_jam ❌ 0/30 62 KB/s 62 KB/s 184s
lpcxpresso11u37 ❌ 0/30 144s
stm32f072disco ❌ 0/30 141s
ra4m1_ek ❌ 0/30 117s
stm32f407disco ❌ 0/30 129s
stm32f723disco ❌ 0/30 16384 KB/s 4096 KB/s 156s
stm32f723disco-DMA ❌ 0/30 16912 KB/s 4096 KB/s 158s
stm32h743nucleo ❌ 0/30 134s
stm32h743nucleo-DMA ❌ 0/30 134s
nrf5340dk ❌ 0/30 120s
stm32g0b1nucleo ❌ 0/30 124s
stm32l476disco ❌ 0/30 141s
stm32u083nucleo ❌ 0/30 129s
nanoch32v203-fsdev ❌ 0/30 103s
nanoch32v203-usbfs ❌ 0/30 103s
ch32v103r_r1_1v0 ❌ 0/30 140s
ch32v307v_r1_1v0-usbhs ❌ 0/30 126s
ch32v307v_r1_1v0-usbfs ❌ 0/30 125s
ch582m_evt ❌ 0/30 130s

@HiFiPhile

Copy link
Copy Markdown
Collaborator

Hi, I see many comment changes, please verify are they intended.

@rhgndf

rhgndf commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Yes they are intended, since there was a hallucination about the x035 and I was concerned there are more, so I got my agent to shrink all of them so they can be verified easily

@HiFiPhile

Copy link
Copy Markdown
Collaborator

Many comments are from Ha Thach like

// CH58X EP registers split into a low block (EP0-4) and a high block (EP5-7). Walk from each
// block's first slot by the 4-byte slot stride (pointer arithmetic off slot 0, so the unused

They shoudn't be removed.

@rhgndf
rhgndf force-pushed the ch32x035 branch 5 times, most recently from 7f1322b to d2b4873 Compare July 4, 2026 09:22
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

Size Difference Report

Because TinyUSB code size varies by port and configuration, the metrics below represent the averaged totals across all example builds.

Note: If there is no change, only one value is shown.

Changes >1% in size

file .text .bss size % diff
dcd_ch32_usbfs.c 1659 ➙ 1688 (+29) 2444 ➙ 2316 (-128) 4103 ➙ 4004 (-99) -2.4%
TOTAL 1659 ➙ 1688 (+29) 2444 ➙ 2316 (-128) 4103 ➙ 4004 (-99) -2.4%
Changes <1% in size

No entries.

No changes
file .text .rodata .data .bss size % diff
audio_device.c 2890 0 1259 1623 4508 +0.0%
cdc_device.c 1236 16 1092 728 1963 +0.0%
cdc_host.c 6399 487 15 971 7594 +0.0%
dcd_ch32_usbhs.c 1892 0 0 481 2373 +0.0%
dcd_ci_fs.c 1924 0 0 1290 3214 +0.0%
dcd_ci_hs.c 1756 0 0 1344 2534 +0.0%
dcd_da146xx.c 3067 0 0 144 3211 +0.0%
dcd_dwc2.c 4223 19 0 265 4506 +0.0%
dcd_eptri.c 2273 0 0 259 2532 +0.0%
dcd_ft9xx.c 3284 0 0 172 3456 +0.0%
dcd_khci.c 1952 0 0 1290 3242 +0.0%
dcd_lpc17_40.c 1481 0 0 648 1805 +0.0%
dcd_lpc_ip3511.c 1463 0 0 264 1683 +0.0%
dcd_mm32f327x_otg.c 1477 0 0 1290 2767 +0.0%
dcd_msp430x5xx.c 1801 0 0 176 1977 +0.0%
dcd_musb.c 2595 0 0 179 2773 +0.0%
dcd_nrf5x.c 2939 0 0 292 3231 +0.0%
dcd_nuc120.c 1096 0 0 78 1174 +0.0%
dcd_nuc121.c 1170 0 0 101 1270 +0.0%
dcd_nuc505.c 0 0 1533 157 1690 +0.0%
dcd_rp2040.c 840 0 764 653 2257 +0.0%
dcd_rusb2.c 2918 0 0 156 3074 +0.0%
dcd_samd.c 1036 0 0 266 1302 +0.0%
dcd_samg.c 1322 0 0 72 1394 +0.0%
dcd_stm32_fsdev.c 2543 0 0 291 2834 +0.0%
dfu_device.c 776 28 712 136 912 +0.0%
dfu_rt_device.c 157 0 134 0 157 +0.0%
dwc2_common.c 603 22 0 0 615 +0.0%
ecm_rndis_device.c 1059 0 1 2759 3818 +0.0%
ehci.c 2763 0 0 6274 7783 +0.0%
fsdev_common.c 180 0 0 0 180 +0.0%
hcd_ch32_usbfs.c 2491 0 0 502 2993 +0.0%
hcd_ci_hs.c 181 0 0 0 181 +0.0%
hcd_dwc2.c 5071 25 1 545 5642 +0.0%
hcd_khci.c 2443 0 0 454 2897 +0.0%
hcd_musb.c 3071 0 0 157 3228 +0.0%
hcd_pio_usb.c 262 0 240 0 502 +0.0%
hcd_rp2040.c 1996 17 4 321 2338 +0.0%
hcd_rusb2.c 2923 0 0 245 3168 +0.0%
hcd_samd.c 2220 0 0 324 2544 +0.0%
hcd_stm32_fsdev.c 3248 0 1 420 3670 +0.0%
hid_device.c 1124 44 997 119 1242 +0.0%
hid_host.c 1246 0 0 1270 2516 +0.0%
hub.c 1384 8 8 30 1419 +0.0%
midi2_device.c 2628 52 1175 561 3211 +0.0%
midi2_host.c 1802 0 0 5921 7723 +0.0%
midi_device.c 1149 0 1007 619 1765 +0.0%
midi_host.c 1339 7 7 3538 4880 +0.0%
msc_device.c 2527 108 2293 804 3331 +0.0%
msc_host.c 1633 0 0 395 2028 +0.0%
mtp_device.c 1713 22 743 589 2309 +0.0%
ncm_device.c 1761 28 819 4393 6167 +0.0%
ohci.c 1925 0 0 2503 4428 +0.0%
printer_device.c 828 0 706 560 1387 +0.0%
rp2040_usb.c 386 35 619 11 1051 +0.0%
rusb2_common.c 160 0 16 0 176 +0.0%
tusb.c 455 0 387 3 457 +0.0%
tusb_fifo.c 855 0 486 0 850 +0.0%
typec_stm32.c 1230 8 2 19 1255 +0.0%
usbc.c 500 2 20 166 688 +0.0%
usbd.c 3530 57 90 355 3948 +0.0%
usbh.c 4967 57 82 1165 6238 +0.0%
usbtmc_device.c 2194 24 68 313 2539 +0.0%
vendor_device.c 639 0 534 559 1197 +0.0%
video_device.c 4433 5 1235 480 4905 +0.0%
TOTAL 123429 1071 17050 49700 174702 +0.0%

rhgndf and others added 6 commits July 19, 2026 14:34
Signed-off-by: HiFiPhile <admin@hifiphile.com>
Signed-off-by: HiFiPhile <admin@hifiphile.com>
@HiFiPhile
HiFiPhile requested a review from Copilot July 19, 2026 16:16
@HiFiPhile

Copy link
Copy Markdown
Collaborator

@claude review

@claude

claude Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 6m 4s —— View job


Review in progress

  • Gather context (PR diff, comments, CLAUDE.md)
  • Understand request (review the PR, focus on comment-shrinking concern raised by @HiFiPhile)
  • Review dcd_ch32_usbfs.c changes (EP4/EP3 handling, register write approach)
  • Check whether attributed comments (Ha Thach's) were inappropriately removed/shrunk
  • Review BSP/board files for CH32X035
  • Post final review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new WCH CH32X035 (and intended CH32X033) TinyUSB port by wiring the MCU into TinyUSB’s MCU selection/dependency system, introducing a new BSP family + board, and extending the shared WCH USBFS DCD to handle CH32X035’s endpoint register/buffer quirks (notably EP4 and combined TX/RX control behavior).

Changes:

  • Add CH32X035 as a supported MCU option and dependency (OpenWCH SDK) and document it in README/docs.
  • Extend dcd_ch32_usbfs.c + WCH register glue for CH32X035-specific register layout/buffer handling.
  • Introduce a new BSP family (hw/bsp/ch32x035) with build/linker/OpenOCD support and a WeAct board definition.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tools/get_deps.py Adds the OpenWCH ch32x035 SDK dependency.
src/tusb_option.h Introduces OPT_MCU_CH32X035 (MCU selection constant).
src/portable/wch/dcd_ch32_usbfs.c Updates WCH USBFS DCD for CH32X035 register/buffer and EP0/EP4 handling.
src/portable/wch/ch32_usbfs_reg.h Adds CH32X035 register header integration and WCH USBFS feature flags.
src/common/tusb_mcu.h Enables WCH USBFS USBIP for CH32X035.
README.rst Documents CH32X033/CH32X035 under supported CPUs.
hw/bsp/ch32x035/wch-riscv.cfg Adds OpenOCD config for WCH RISC-V + WLinkE.
hw/bsp/ch32x035/system_ch32x035.h Adds system header for CH32X035 BSP.
hw/bsp/ch32x035/system_ch32x035.c Adds system clock implementation for CH32X035 BSP.
hw/bsp/ch32x035/linker/ch32x035.ld Adds linker script for CH32X035 memory layout.
hw/bsp/ch32x035/family.mk Adds Make-based family build configuration for CH32X035.
hw/bsp/ch32x035/family.cmake Adds CMake-based family build configuration for CH32X035.
hw/bsp/ch32x035/family.c Adds board API + interrupt handlers for CH32X035 BSP.
hw/bsp/ch32x035/ch32x035_it.h Adds interrupt handler declarations for CH32X035 BSP.
hw/bsp/ch32x035/ch32x035_conf.h Adds WCH peripheral library config header for CH32X035 BSP.
hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.mk Adds board-specific Make overrides (clock, memory sizes).
hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.h Adds WeAct board pin/LED definitions.
hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.cmake Adds WeAct board-specific CMake definitions.
examples/device/uac2_speaker_fb/src/audio_debug.py Updates PID used by the audio debug helper.
examples/device/uac2_speaker_fb/skip.txt Adds CH32X035 (and other WCH MCUs) to skip list for this example.
examples/device/uac2_headset/skip.txt Adds CH32X035 (and other WCH MCUs) to skip list for this example.
examples/device/net_lwip_webserver/skip.txt Adds CH32X035 to skip list.
examples/device/midi_test_freertos/skip.txt Adds CH32X035 to skip list.
examples/device/hid_composite_freertos/skip.txt Adds CH32X035 to skip list.
examples/device/cdc_msc_freertos/skip.txt Adds CH32X035 to skip list.
examples/device/audio_test/skip.txt Adds CH32X035 to skip list.
examples/device/audio_test_multi_rate/skip.txt Adds CH32X035 to skip list.
examples/device/audio_test_freertos/skip.txt Adds CH32X035 to skip list.
examples/device/audio_4_channel_mic/skip.txt Adds CH32X035 to skip list.
examples/device/audio_4_channel_mic_freertos/skip.txt Adds CH32X035 to skip list.
docs/reference/dependencies.rst Adds the hw/mcu/wch/ch32x035 dependency entry.
docs/reference/boards.rst Adds WeAct CH32X035 board entry to the reference list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/portable/wch/dcd_ch32_usbfs.c
Comment thread src/portable/wch/dcd_ch32_usbfs.c
Comment thread hw/bsp/ch32x035/system_ch32x035.c
Comment thread hw/bsp/ch32x035/family.cmake
Comment thread src/tusb_option.h
Signed-off-by: HiFiPhile <admin@hifiphile.com>
@HiFiPhile

Copy link
Copy Markdown
Collaborator

Thank you, please check if CH32X035 works correctly with latest changes.

@rhgndf

rhgndf commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

I went ahead and added the change to restrict manual toggling to EP4.
The firmware works correctly on my ch32x035

@HiFiPhile

Copy link
Copy Markdown
Collaborator

Are you sure only EP4 needs manual toggle on CH32X035 ? From the manual there is no auto toggle bit at all.

Also ch582 needs manual toggle:

  // CH58x's hardware AUTO_TOG does not stay in sync (notably across clear-stall and multi-packet
  // bulk transfers), causing data-toggle mismatch and bus resets. Drive the toggle manually in
  // the ISR instead. CH32V103/V20x/V307 keep AUTO_TOG (this macro is undefined for them).

@rhgndf

rhgndf commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Yes, I'm quite sure since it is indeed in the manual under TOG_AUTO/AUTO_TOG

CH32X035

Page 223 for EP1, this exists for all EPs not 4
image

Page 226 for EP4
image

CH58x

Page 128
image

EP4 doesn't have auto toggle for both chips.

  // notably across clear-stall

This issue was fixed too: https://github.com/rhgndf/tinyusb/blob/e80049bfb305ce2164310a510af78814234ede1b/src/portable/wch/dcd_ch32_usbfs.c#L600
Someone with a CH58x would need to check whether that fixes it

For the multi-packet bulk transfers, it works with cdc_msc_throughput with the results:

tog speed
manual tog 581kB/s
auto tog 631kB/s

Modified endpoint response handling for USBFS to ensure proper ACK/NAK behavior based on transfer validity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants