summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-06-15dcd/musb: name pipe0_state_t, use local pointer, group struct fieldshathach
Pure cleanup, no behavior change: - Extract the EP0 control-transfer state into a named pipe0_state_t typedef instead of an anonymous nested struct, and access it through a local pipe0_state_t* in the functions that touch it repeatedly. - Group the pipe0 fields so the two bools sit together and the larger tusb_control_request_t deferred_setup is last. - Reword the deferral comments: "coalesced" -> "combined". Note: separating the edpt0_xfer DATA_IN/DATA_OUT case (dispatch on state instead of dir_in) was attempted and reverted — it breaks ADI MUSB enumeration. usbd can arm the opposite-direction status while pipe0 is still in a DATA state, and only dir-dispatch routes that correctly; a comment on the combined case records this. Verified: HIL pass on ek_tm4c123gxl and max32666fthr (13/13 each). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13hil: make serial write timeout fatalhathach
pyserial's posix write() raises SerialTimeoutException after partial progress with the byte count lost, so the retry loop re-sent from the same offset and could duplicate bytes on the wire — surfacing as bogus data-mismatch failures that look like device firmware bugs. write_timeout is already a total per-call deadline, so the loop added duplication risk without extending the budget: write once and treat a timeout as fatal. Default bumped 2 -> 10 s to keep the old overall bound; HIL_SERIAL_WRITE_DEADLINE removed. The per-character CLI loops keep their existing pacing (the 2 ms sleep between single-byte writes already spaces them on the wire); no unbounded ser.flush()/tcdrain is added. Review follow-up for #3643 (hil_test.py l.257/264 findings). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: clear rxrdy_consumed when stalling EP0hathach
The actual-STALL path (no deferred SETUP) forced EP0 to IDLE but left rxrdy_consumed set if the aborted transfer had parked RXRDY via NAK flow control (e.g. a rejected OUT-data request in DATA_OUT). A subsequent SETUP IRQ would then hit the parked-gate early return and be ignored, relying on SentStall/SetupEnd to clear the flag first. Clear it here so recovery never depends on that ordering. Addresses Copilot review on #3699. Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: replay deferred SETUP instead of stalling EP0hathach
dcd_edpt_stall(EP0 OUT) discarded the deferred SETUP and armed SendStall. A deferred SETUP can only exist once the old transfer's status stage was seen on the wire, so the request usbd is rejecting (class callback failing at CONTROL_STAGE_DATA) already succeeded host-side and the hardware already ACKed the next SETUP - the STALL would land on that innocent request, which then fails host-side without any tud callback ever seeing it. Skip the stall and replay the deferred SETUP; the rejected transfer needs no wire-level stall since it is already over. Review follow-up for #3643 (dcd_musb.c l.860 finding). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: gate stale EP0 RXRDY interrupts with rxrdy_consumedhathach
The deferral path drains the SETUP but leaves RxPktRdy set, and the SETUP's IRQ latches after the ISR's clear-on-read intr_tx read - so a second process_ep0 pass (same ISR, via the intr_tx re-read merge) is guaranteed and misreads the leftovers: count0==0 fires a spurious DATA OUT completion, the replay's RXRDYC write turns the second pass into a phantom csrl==0 DATA IN completion, and a zero-length replay re-enters the deferral case on a drained FIFO (count0 assert or garbage saved as a SETUP). The registers cannot expose the staleness: RxPktRdy and count0 read unchanged until ServicedRxPktRdy is written. Track it in software: rxrdy_consumed means "RxPktRdy is set in hw but its packet was already consumed". Set wherever a drained packet's RXRDY is intentionally left set (OUT/zero-length flow-control parks, every DATA OUT drain awaiting the next arm, the deferral path); cleared at every RXRDYC write site (edpt0_xfer arms, dcd_set_address, STALLED/SETEND recovery, bus reset). The RXRDY block returns early while parked. Replayed IN requests skip the RXRDYC in pipe0_start_setup and keep the packet parked until the edpt0_xfer(DATA IN) arm acks it (before loading the shared FIFO), so the stale pass sees RXRDY+parked instead of csrl==0. The normal IDLE path is unchanged - master never re-entered these windows because the single SETUP edge was always consumed by the pass that parked it; the deferral is what introduced a pending second pass. Review follow-up for #3643 (dcd_musb.c l.516 finding). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: fix deferred-SETUP replay racing usbd's status callhathach
STATUS_OUT_PENDING conflated "edpt0_xfer(STATUS OUT) called, awaiting confirm IRQ" with "confirm IRQ seen, awaiting edpt0_xfer". The deferral path completed the status and replayed the saved SETUP from the ISR in both flavors; in the IRQ-first one, usbd's still- outstanding edpt0_xfer(STATUS OUT) for the old transfer (queued via status_stage_xact) then landed in the replayed transfer's state and corrupted it: NULL pipe0.buf armed plus RXRDYC, so the host's next DATA OUT drained through a NULL pointer. usbd processes EP0 XFER_COMPLETE events unconditionally, so nothing downstream defuses it. Split the state into STATUS_OUT_PENDING_XFER / _IRQ. The deferral completes and replays only in PENDING_XFER (old transfer already retired); in PENDING_IRQ it only holds the SETUP and the usbd-driven edpt0_xfer fires the completion and replays. The DATA_IN deferral now synthesizes PENDING_IRQ (its remain==0 invariant asserted: a SETUP before DataEnd raises SetupEnd instead), which also makes the old deferred-promotion in the csrl==0 DATA_IN case unreachable - dropped. Assert the drain buffer before the DATA OUT FIFO read as a cheap backstop for this corruption class. Review follow-up for #3643 (dcd_musb.c l.503 finding). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: check SentStall/SetupEnd before DATAEND guardhathach
MUSBMHDRC 21.1.5 requires the EP0 service routine to check SentStall and SetupEnd first; the early DATAEND return ran before both, and SentStall is most likely to fire exactly while DataEnd may still read back set (auto-STALL after DataEnd, 21.1.7), which would skip the recovery. The guard also moves below the RXRDY block so a coalesced DATAEND|RXRDY read cannot swallow a SETUP on cores where the CPU-set-only DataEnd bit reads back 1; the comment documents the vendor-dependent read-back. Review follow-up for #3643 (dcd_musb.c l.445 finding). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: replace deferral goto with per-state handlinghathach
The goto jumped into the csrl==0 completion switch with RXRDY still set, making its "When CSRL0 is zero" guard comment untrue on that path. Handle each deferral state in a self-contained switch instead; the csrl==0 switch is now only reached with csrl==0 and its comment is truthful again. Behavior unchanged. Review follow-up for #3643 (dcd_musb.c l.523 finding). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: restore EP0 OUT RXRDY flow-control commenthathach
The pre-existing comment explaining why the OUT branch does not ack RxPktRdy was dropped when the SETUP handling moved into pipe0_start_setup(). It is load-bearing: acking before edpt0_xfer() arms the drain buffer would let the host send data with nowhere to put it. Restore it with the databook-deviation rationale so the branches don't get "unified" later. Review follow-up for #3643 (dcd_musb.c l.116 finding). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-13dcd/musb: extract pipe0_read_setup() helperhathach
The 8-byte EP0 SETUP drain (count0 assert + two FIFO word reads via a union) was duplicated verbatim between the IDLE case and the deferral case; a future fix applied to one copy but not the other would only show up on the rare deferred-race path. Share one helper. count0 is now read inside the only remaining user (DATA OUT drain). Review follow-up for #3643 (dcd_musb.c l.507 finding). Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-11Merge remote-tracking branch 'origin/master' into musb_ep0_racehathach
2026-06-11Merge pull request #3693 from hathach/claude/review-model-opusHa Thach
ci(claude-review): run auto review on Opus (claude-opus-4-8)
2026-06-11ci(claude-review): run auto review on Opus (claude-opus-4-8)hathach
The review action currently runs on the default Sonnet 4.6. On PR #3643 (musb EP0 race) it posted "No issues found" while an Opus pass on the same diff surfaced substantive questions (ISR-boundary RXRDY lifetime, regression scope of the DATA-state split). Pin the reviewer to claude-opus-4-8 for higher-signal reviews; subagents keep their cheaper default models. Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-11Merge remote-tracking branch 'origin/master' into musb_ep0_racehathach
# Conflicts: # test/hil/hil_test.py
2026-06-11Merge pull request #3692 from hathach/claude/bump-claude-review-max-turnsHa Thach
ci(claude-review): raise --max-turns 20 -> 50
2026-06-11Merge pull request #3636 from hathach/stm32c5Ha Thach
2026-06-11ci(claude-review): raise --max-turns 20 -> 50hathach
The Claude Code Review action runs /code-review:code-review with a hard --max-turns cap. On large PRs (e.g. #3636 "add stm32c5 support", 29 files / +1689), the agent exhausts 20 turns exploring the diff before it can produce and post its review, so the SDK returns an error and the claude-review check fails red with: Reached maximum number of turns (20) Raise the cap to 50 so port-sized PRs complete and post their review. Cost scales with tokens, not the cap: a finished review pays the same whether the ceiling is 25 or 50 — the cap only bites when the agent would otherwise be force-stopped mid-run. Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-11Merge remote-tracking branch 'origin/master' into stm32c5hathach
# Conflicts: # README.rst
2026-06-11README: use emoji for Supported CPUs status marks (#3691)Ha Thach
Replace the ✔/⚠/✖ status symbols in the Supported CPUs table and its legend with ✅ (Supported), 🟡 (Partial support) and ❌ (Not supported by hardware) for clearer at-a-glance scanning.
2026-06-11Fix stm32f723disco host/cdc_msc_hid HIL: UART RX starvation + DWC2 DMA ↵Ha Thach
split-IN NAK storm (#3677) Fix stm32f723disco host HIL: UART RX starvation + DWC2 split bulk NAK/XactErr handling (#3677) stm32f7 BSP — UART RX starvation - The host console USART shared interrupt priority with the USB OTG ISR, so a long OTG interrupt could starve RXNE and drop received bytes. Raise the USART RX IRQ above OTG_FS/OTG_HS in both the bare-metal and FreeRTOS init paths, guarded by #ifdef UART_ID so boards without a UART console keep the default OTG priority. dwc2 host — split NAK/XactErr handling - Slave mode: a persistently-NAKing split bulk/control IN poll re-armed the start-split immediately, storming the ISR and starving task context. Throttle by disabling the channel and re-arming on the resulting halt (no frame deferral). - Buffer-DMA mode: a pure split bulk-OUT NAK was unhandled, leaving the channel halted and stalling the transfer — the dominant cause of CDC echo truncation. Handle it by rewinding the buffer pointers and retrying the start-split (Programming Guide v4.20a 5.1.4.2). - Buffer-DMA mode: a split bulk-OUT XactErr was retried immediately, exhausting HCD_XFER_ERROR_MAX before the transient cleared. Throttle via channel_disable + re-arm to give the hub TT a recovery gap, mirroring slave mode. - All three are scoped to split transfers (hcsplt.split_en); non-split NAK/XactErr keep the core-handled / immediate-retry behavior. The OUT XactErr throttle also excludes periodic split, where channel_disable() is a no-op and would wedge the channel. The nak_disabled flag is generalized to retry_disabled and honors xfer->closing so an endpoint close during a throttled retry tears down cleanly. Verified on stm32f723disco HIL (slave + CFG_TUH_DWC2_DMA_ENABLE): host/cdc_msc_hid, msc_file_explorer, and device_info all pass on both variants; DMA CDC echo went from ~15-25% raw failure to 10/10 clean.
2026-06-11HIL: replace build.flags_on with named build variants (#3687)Ha Thach
* test/hil: replace build.flags_on with named variant schema Boards declare build variants as `variant: [{name, flags}]` instead of `build.flags_on`. The variant `name` is the build dir (cmake-build-<name>) and the HIL report row; `flags` is the raw CFLAGS string (-D...=1) injected via CFLAGS_CLI. No `variant` => a single build named after the board. - build.py: --build-name <name> (dir) + --cflag=<token> (raw CFLAGS, repeatable, =form survives the matrix's shell word-splitting); drop -f1/CFLAGS wrapping. - hil_ci_set_matrix.py: emit one build arg per variant. - hil_test.py: iterate variants; report row + build dir = variant name. - hil_ci.sh: copy all cmake-build-<board>* dirs for -b runs. - get_deps.py: accept (ignore) --build-name/--cflag from matrix args. - tinyusb.json: migrate all 6 flags_on boards to variant. * board_test: park CI build with busy spin instead of wfe
2026-06-10Merge pull request #3690 from hathach/claude/board-test-idle-parkHa Thach
hil: park boards with idle board_test instead of erasing flash
2026-06-09Merge pull request #3686 from hathach/update-hil-poolHa Thach
HIL: add stm32u083nucleo and post test report as PR comment
2026-06-09Merge pull request #3681 from hathach/cdc_ctrl_bufHa Thach
host/cdc: use local control buffer
2026-06-09test/hil: erase MCU after tests; show throughput speeds in reporthathach
Teardown: instead of flashing device/board_test (a USB-less blink loop that keeps the MCU busy-looping), erase the first flash sector (vector table) so the board faults to idle after its tests — no USB, lower power, faster. Per-flasher erase_<name>: openocd/openocd_adi `flash erase_sector 0 0 0`; stlink `--erase 0`; jlink erases the sector at the flash origin read from the ELF (pure-Python, new elf_flash_origin); esptool `erase_region 0x0 0x4000`; lm4flash writes a 4 KB all-0xFF blank image (lm4flash erases before programming, so the first sector ends up blank). device/board_test flash remains a fallback for flashers with no erase_ function. The teardown is no longer a report column (it's cleanup). Report: cdc_msc_throughput and msc_file_explorer[_freertos] now return a compact read/write speed shown in their report cell instead of the pass tick (e.g. "C 652k/422k M 1.1M/783k", "rd 1.2MB/s"). test_example returns an optional metric; render_matrix shows it verbatim. Firmware lookup factored into find_firmware (reused by the erase teardown). Verified on the rig (stm32f723disco, jlink): erase disables the board in 0.8 s and it disappears from the bus; the throughput cell shows live speeds. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08test/hil: use ⚪ for skipped in HIL reporthathach
Neutral white circle for skipped, giving a ✅/❌/⚪ pass/fail/skip set. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08test/hil: use ✅/❌ emoji for HIL report pass/failhathach
Colored emoji render green/red in the GitHub PR comment, far more visible than the monochrome ✔/✖ dingbats. Skip stays ➖. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08ci: demote Average Code Size Metrics title to h2hathach
metrics.md (write_combine_markdown) is also used as the PR size comment when there is no base-metrics baseline; use h2 for its title too so the sticky comment heading is consistent (and not oversized) in that fallback case. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08ci: demote sticky-comment report headings to h2; rename HIL reporthathach
The Size Difference Report and HIL comments rendered their titles at h1, which is oversized inside a PR comment. Use h2 for both titles (with subsections demoted to h3 to keep the hierarchy), and rename the HIL comment from "HIL test results" to "Hardware-in-the-loop (HIL) Test Report" for consistency. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08ci: include hil-hfp-iar (IAR) results in the HIL PR commenthathach
hil-hfp-iar runs hil_test.py on hfp.json built with IAR on its own rig. Upload its report as the hil-report-hfp-iar artifact and add the job to the hil-report combine job's needs, so the sticky comment shows a third table for the IAR rig alongside tinyusb.json and hfp.json (gcc). The combine gate now runs if either HIL job produced results. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08test/hil: clear HIL report up front on a fresh runhathach
The report sidecar lives in a persistent dir (it survives the CI workspace clean so accumulation works across run attempts). A full run is "fresh" and must not merge prior state, but previously fresh only avoided *loading* the json at merge time — if a fresh run crashed before writing the report, the stale json/md from an earlier run lingered and a retry (fresh=False) could merge it, or the always() upload could post it. Delete hil_report.json/.md at the start of a fresh run so prior results can never leak. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08test/hil: accumulate HIL report across re-runs; post as sticky PR commenthathach
hil_test.py persists results in a hil_report.json sidecar and regenerates hil_report.md from it. A full run starts fresh; a re-run (--skip-board / -bt, i.e. the .skip file) merges into the existing report so already-passed boards/tests are preserved while only re-run cells update. The report dir is configurable via HIL_REPORT_DIR. build.yml: each HIL rig writes the report to a workspace-sibling dir that survives the per-attempt workspace clean, and uploads it as an artifact. A new hil-report job merges the rigs' reports into one sticky PR comment (marocchino) with one table per rig. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08presets,hil: keep Ninja Multi-Config; make HIL find its outputhathach
Per review (HiFiPhile): Ninja Multi-Config is needed for IAR, otherwise the optimization level can't be lowered to none for debug. Revert gen_presets.py back to Ninja Multi-Config (keeping only the cmake-build-<board> binaryDir change), and instead teach hil_test.py to locate <ex>.elf whether it sits directly in the example dir (single-config) or under a per-config subdir like RelWithDebInfo/ (multi-config). Verified: stm32u083nucleo passes 13/13 remote HIL with a multi-config preset build (rsync preserves the RelWithDebInfo/ subdir; the resolver finds it). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08test/hil: report board x test results as a markdown matrixhathach
hil_test.py now writes hil_report.md and prints it to stdout: rows are boards, columns are tests (bare example names), cells are pass/fail/skip. test_example returns a per-test status, test_board collects a board x test grid (one row per flags-on variant), and main() renders an aligned table. A missing binary counts as skipped. hil_ci.sh copies the report back from the remote after a run; hil_report.md is gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08gitattributes: keep shell scripts LF under core.autocrlfhathach
With core.autocrlf=true, *.sh files were checked out / restored with CRLF line endings, which breaks bash ($'\r': command not found; set: pipefail: invalid option). Pin *.sh to eol=lf so shell scripts stay LF in the working tree regardless of autocrlf. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08Fix formatting in README.rst table (#3685)Ha Thach
2026-06-08Fix formatting in README.rst tableHa Thach
2026-06-08test/hil: add stm32u083nucleo to pool and expose ~/bin on remote PATHhathach
Add stm32u083nucleo to the active boards in tinyusb.json, flashed via the stlink flasher (onboard ST-Link + STM32CubeProgrammer); ci's openocd build has no STM32U0 flash driver. STM32_Programmer_CLI lives in ~/bin on ci, which the remote `bash -s` shell in hil_ci.sh did not have on PATH, so add $HOME/bin to its PATH export (matching the GHA runner .path). Verified remote: 13/13 device tests pass on ci.lan. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08tools/gen_presets: build into cmake-build-<board> with single-config Ninjahathach
Change the default configure preset binaryDir from build/<board> to cmake-build-<board> (the dir name HIL expects) and switch the generator from Ninja Multi-Config to single-config Ninja. Multi-Config nests binaries under a RelWithDebInfo/ subdir, which hil_test.py does not look in; single-config emits device/<ex>/<ex>.elf so preset-built firmware is directly consumable by `hil_test.py -B examples`. Regenerated BoardPresets.json (also picks up the tracked ch32v103c_bluepill board that was missing from presets). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08stm32u0: implement board_get_unique_id from UID_BASEhathach
Previously stm32u0 had no board_get_unique_id(), so it fell back to the weak default in hw/bsp/board.c and every board reported the placeholder USB serial 0123456789ABCDEF. HIL identifies boards by USB serial, so a non-unique serial collides on a multi-board rig. Read the 96-bit unique ID from UID_BASE, mirroring stm32u5. Verified on stm32u083nucleo: now enumerates as 300044000D5036394E373620. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-08ci: post auto-review findings to the PR (#3684)Ha Thach
Add --comment so the auto-review is actually posted on the PR.
2026-06-06host/cdc: use local control bufferHiFiPhile
2026-06-05add tm4c123x evk to hill pool (#3676)Ha Thach
* add ek_tm4c123gxl to the hil pool, flashing with lm4flash
2026-06-05ci: carry metrics baseline forward on no-code-change pushes (#3678)Ha Thach
* ci: carry metrics baseline forward on no-code-change pushes The code-metrics job is gated on code_changed and only uploads the metrics-tinyusb artifact on push, so a workflow/docs-only push to master (e.g. removing an unrelated workflow) leaves the latest master Build run without a baseline. PRs download the baseline from the latest master run, so the size comparison then finds nothing and silently falls back to absolute sizes. Add a small metrics-carry-forward job that, on a non-code-change push, downloads the previous metrics-tinyusb artifact and re-publishes it, so the latest run always carries a usable baseline. Carry-forward runs re-upload too, so the baseline chains across consecutive no-code pushes (bounded by artifact retention).
2026-06-04Remove Sponsor Triage workflow (migrated to hathach/hathach) (#3675)Ha Thach
This personal automation now lives in the hathach/hathach repo alongside the other personal project-sync workflows; it has no place in the tinyusb library. Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-04Merge pull request #3666 from hathach/dwc2_postfixHa Thach
dwc2 postfixes
2026-06-04Merge pull request #3653 from hathach/ch32_warningHa Thach
add device specific issues
2026-06-04dwc2: cleanup setup_packet pointer cast (review feedback)hathach
Cast DOEPDMA0 through uintptr_t and use sizeof(tusb_control_request_t) instead of the magic constant 8, matching project convention. Add a reference to Programming Guide v4.20a 9.1.2.1 for the DOEPDMAn-8 rule. Addresses Copilot review comment; no functional change. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-04Update setup buffer size definition based on DMA configurationhathach
2026-06-04Merge branch 'master' into dwc2_postfixhathach