Skip to content

feat(ch32v): CH32V003 bring-up Phase 1 verified + probe docs + hw-gated tests (#1208) - #1210

Merged
zackees merged 1 commit into
mainfrom
fix/1208-ch32v-probe-classification
Jul 29, 2026
Merged

feat(ch32v): CH32V003 bring-up Phase 1 verified + probe docs + hw-gated tests (#1208)#1210
zackees merged 1 commit into
mainfrom
fix/1208-ch32v-probe-classification

Conversation

@zackees

@zackees zackees commented Jul 29, 2026

Copy link
Copy Markdown
Member

Phase 1 of the CH32V003 bench bring-up (#1208). The CH32V pipeline had only ever been compile-verified in CI — this is the first time it has been run against a real toolchain install, with the numbers recorded.

Phase 1 results — verified locally

fbuild build tests/platform/ch32v003 -e ch32v003:

Check Result
xPack riscv-none-elf-gcc 14.2.0-3 download + checksum + validate
ISA / ABI -march=rv32ec_zicsr -mabi=ilp32e
ELF header Class: ELF32, Machine: RISC-V, Flags: 0x9, RVC, RVE, soft-float ABI
rv32ec/ilp32e libgcc multilib present ✅ (link succeeds)
SYSCLK_FREQ_* injected (#1119 guard) -DSYSCLK_FREQ_48MHZ_HSI=48000000
Flash budget 8896 / 16384 B (54.3%)
RAM 1180 / 2048 B (57.6%)

RVE in the ELF flags confirms the E base (16 registers), so the multilib really is RV32EC and not a silent rv32i fallback.

The RAM figure is worth flagging: a bare blink already uses 57.6% of the 2 KB, leaving ~868 bytes. That is a hard constraint on the FastLED port (FastLED/FastLED#3755).

Unblocking the local dev build

Phase 1 was initially blocked by a silent ~10-minute hang. Root cause:

The readiness poll after a daemon spawn was bounded by iteration countfor _ in 0..100 with a 100 ms sleep, documented as "up to 10 seconds". That equivalence only holds when health() returns instantly, which is true when the port is refused. When the endpoint times out instead, each health() burns its own ~2 s request timeout, so one attempt takes ~3.5 min and all three take ~10 min — with no output, indistinguishable from a wedged build.

That state is reachable in normal development: cargo run -p fbuild-cli does not build fbuild-daemon, so daemon_executable_hint() finds no sibling binary and falls back to PATH. On this host that resolved to a PyPI-installed v2.5.2 daemon while the local CLI was v2.5.4. Because daemon_endpoint_key() hashes CARGO_PKG_VERSION (#1009), the two derived different ports (49411 vs 52289) and could never rendezvous.

Fix: make the budget wall-clock, and put the resolved daemon path plus that explanation into the error instead of failing silently.

Also in this PR

Hardware-gated wlink tests (per the #839 ignored-test policy):

  • try_install_wlink_from_pinned_release — network only, no hardware; exercises pinned-asset + SHA-256 + extract, so a rotted URL or stale checksum is caught without a probe on the bench.
  • try_wlink_status_detects_ch32v003 — Phase 0 gate.
  • try_flash_real_ch32v003 — Phase 2, with a 16 KB size guard. Its output states explicitly that a successful flash is not proof of bring-up; the blink still has to be observed.

Probe-classification hardening. family_from_usb_profile now checks probe identity before the usb_uart_bridge role. purpose and role are orthogonal in the registry schema, so a probe that also exposes a CDC UART would have matched the bridge arm first and been handed ESP32 BOOT/EN autoreset semantics — into a probe with no such transistor pair.

To be precise about severity: no live profile currently carries that combination (0 of 3524 — all 5 probe entries set purpose=probe and role=debug_probe), so the old ordering already classified the WCH-LinkE correctly. This is hardening against a representable schema state, not a fixed live defect.

The cfg(test) 0x1A86 wildcard was wrong, though: it claimed the WCH-LinkE PIDs were ESP32 external UARTs, contradicting the published registry. Narrowed, with a regression test.

Docs. Probe bring-up procedure in the CH32V README (RV/DAP button toggle, Zadig WinUSB binding, wlink status, V003 SWIO wiring, the charge-only-cable trap) and a WCH-LinkE extension to the DTR/RTS matrix.

Finding that changes #1208's plan

No FastLED/boards PR is needed. #1208 Phase 3 called for adding the WCH-LinkE USB IDs upstream. They are already published with correct semantics:

1a86:8010  purpose=probe  role=debug_probe  family=wch-link      platform=ch32v  prio=1000
1a86:8012  purpose=probe  role=debug_probe  family=wch-link-dap  platform=ch32v  prio=1000
1a86:55e0  purpose=bootloader  role=bootloader_dfu  family=wchisp  platform=ch32v

Still open

Phases 0 and 2 are not done. No WCH device enumerates on this host — the only candidate is an Unknown USB Device (Device Descriptor Request Failed) under a null VID 0000:0002, which is a cable/port/hub fault rather than a driver issue. The measured-blink milestone still requires a working probe.

Test plan

  • soldr cargo test -p fbuild-serial -p fbuild-deploy --lib → 260 + 178 passed, 0 failed
  • soldr cargo test -p fbuild-cli --bin fbuild → 262 passed, 0 failed
  • soldr cargo clippy -p fbuild-serial -p fbuild-deploy -p fbuild-cli --all-targets -- -D warnings → clean (only a pre-existing clippy.toml/Cargo.toml MSRV-mismatch note)
  • soldr cargo fmt --all
  • End-to-end CH32V003 build as tabulated above

Refs #1208. Companion FastLED port: FastLED/FastLED#3755. Meta: #1209.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved detection of WCH-LinkE debug probes so they are no longer mistaken for ESP32 UART bridges.
    • Enhanced daemon startup checks with clearer timeout warnings and actionable diagnostics when startup fails.
  • Documentation

    • Added CH32V bring-up guidance covering WCH-LinkE setup, Windows drivers, wiring, connectivity checks, and CH32V003 limitations.
    • Clarified USB CDC and serial-port behavior for WCH-LinkE and CH32V003 boards.

…ed tests (#1208)

Phase 1 of the CH32V003 bench bring-up. The CH32V pipeline had only ever
been compile-verified in CI; this validates it against a real toolchain
install and records the numbers.

Verified locally (fbuild build tests/platform/ch32v003 -e ch32v003):

  - xPack riscv-none-elf-gcc 14.2.0-3 downloads, checksums, and validates
  - -march=rv32ec_zicsr -mabi=ilp32e; ELF reports "RVC, RVE, soft-float"
  - rv32ec/ilp32e libgcc multilib present (link succeeds)
  - -DSYSCLK_FREQ_48MHZ_HSI=48000000 injected (#1119 guard holds)
  - Flash 8896 / 16384 B (54.3%), RAM 1180 / 2048 B (57.6%)

Changes:

- Unblock the local dev build. The readiness poll after a daemon spawn
  was bounded by iteration count (100 x 100ms, documented "10 seconds"),
  which only holds when health() returns instantly — true when the port
  is refused. When the endpoint instead times out, each health() burns
  its own ~2s request timeout, making one attempt ~3.5 min and all three
  ~10 min with no output. That is reachable whenever a locally-built CLI
  spawns a PATH-resolved fbuild-daemon of a different version, since
  daemon_endpoint_key hashes CARGO_PKG_VERSION (#1009) so the two derive
  different ports and can never rendezvous. Made the budget wall-clock
  and put the resolved daemon path plus that explanation in the error.

- Hardware-gated wlink tests: probe status, real flash (with a 16 KB
  size guard), and a network-only install test that exercises the pinned
  asset + checksum + extract path without needing a probe.

- Order probe identity ahead of the usb_uart_bridge role in
  family_from_usb_profile. purpose and role are orthogonal in the
  registry schema, so a probe that also exposes a CDC UART would have
  matched the bridge arm first and been handed ESP32 BOOT/EN autoreset
  semantics. No live profile currently carries that combination (0 of
  3524), so this is hardening, not a fixed live defect.

- Narrow the cfg(test) 0x1A86 wildcard, which claimed the WCH-LinkE
  probe PIDs (8010/8012) were ESP32 external UARTs. The published
  registry has them as purpose=probe/role=debug_probe, so the fixture
  contradicted production.

- Document the probe bring-up (RV/DAP button toggle, Zadig WinUSB
  binding, wlink status, V003 SWIO wiring) and extend the DTR/RTS matrix.

Note: no FastLED/boards PR is needed — 1a86:8010 and 1a86:8012 are
already published with correct probe semantics.

Phases 0/2 (probe enumeration, measured blink) remain open: no WCH
device enumerates on this host yet.

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6eeafc5c-9a55-4264-bfc2-7bb07383dd3a

📥 Commits

Reviewing files that changed from the base of the PR and between d709b18 and 9104b60.

📒 Files selected for processing (5)
  • crates/fbuild-build-mcu/src/ch32v/README.md
  • crates/fbuild-cli/src/daemon_client.rs
  • crates/fbuild-deploy/src/wlink.rs
  • crates/fbuild-serial/src/boards.rs
  • docs/usb-cdc-control-line-matrix.md

📝 Walkthrough

Walkthrough

The PR prioritizes WCH-LinkE probe classification, adds ignored CH32V003 installation/status/flashing tests, documents CH32V bring-up, and changes daemon readiness polling from iteration-based retries to deadline-based health checks with expanded diagnostics.

Changes

CH32V bring-up

Layer / File(s) Summary
WCH-LinkE probe classification
crates/fbuild-serial/src/boards.rs
Probe purpose, debug-probe role, and WCH-LinkE VID/PID pairs now resolve to CdcAcmBridge before UART-bridge matching, with unit coverage.
WCH-LinkE installation and flashing validation
crates/fbuild-deploy/src/wlink.rs
Ignored integration tests verify pinned wlink installation, CH32V003 detection, firmware-size limits, and flashing.
CH32V003 bench setup
crates/fbuild-build-mcu/src/ch32v/README.md, docs/usb-cdc-control-line-matrix.md
Documentation covers probe modes, WinUSB setup, wiring, status checks, serial output, and CH32V003 device constraints.

Daemon readiness

Layer / File(s) Summary
Deadline-based daemon readiness
crates/fbuild-cli/src/daemon_client.rs
Daemon health polling uses READINESS_TIMEOUT_SECS and reports timeout warnings plus runtime diagnostics after failed spawn attempts.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1208-ch32v-probe-classification

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zackees
zackees merged commit 4544faa into main Jul 29, 2026
86 of 93 checks passed
@zackees
zackees deleted the fix/1208-ch32v-probe-classification branch July 29, 2026 00:35
@fastled-project-sync fastled-project-sync Bot moved this to Triage in FastLED Tracker Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

1 participant