<feed xmlns='http://www.w3.org/2005/Atom'>
<title>tinyusb.git/.claude, branch claude/add-systemview-debug</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://cgit.235523.xyz/tinyusb.git/atom/.claude?h=claude%2Fadd-systemview-debug</id>
<link rel='self' href='http://cgit.235523.xyz/tinyusb.git/atom/.claude?h=claude%2Fadd-systemview-debug'/>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/'/>
<updated>2026-09-03T22:11:02Z</updated>
<entry>
<title>sysview: fix cycle-2 review findings</title>
<updated>2026-09-03T22:11:02Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-09-03T22:11:02Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=f9c0c1239250399bc41347b9160bd95721f86dca'/>
<id>urn:sha1:f9c0c1239250399bc41347b9160bd95721f86dca</id>
<content type='text'>
- sysview_ci.py: wait() after kill() on the OpenOCD/nc grace-period
  timeout, so a still-exiting process can't hold the probe when the
  recovery flash starts
- SKILL.md: point the OpenOCD and post-mortem recipes at
  examples/device/cdc_msc_freertos, where build-sv actually lands
- stm32f401blackpill: override SYSVIEW_BUFFER_SIZE_DEFAULT to 4096;
  the 64 KiB part overflows RAM by 12952 bytes at the family's 65536
  default under SYSVIEW=4
- get_deps.py: align the SystemView entry's continuation lines
</content>
</entry>
<entry>
<title>target-debug: route scheduling and timing questions to the sysview skill</title>
<updated>2026-09-03T21:21:57Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-11T10:08:05Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=7474173b075ae656ed16dea5dcb3f830349f3450'/>
<id>urn:sha1:7474173b075ae656ed16dea5dcb3f830349f3450</id>
<content type='text'>
RTOS/ISR scheduling, CPU load and latency-profile questions go to sysview
first; target-debug keeps the failure-hunting techniques. CLAUDE.md points
at the new skill.
</content>
</entry>
<entry>
<title>sysview: add the skill - headless SystemView capture and reporting</title>
<updated>2026-09-03T21:21:57Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-09-03T10:14:39Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=463ff1f3d2d97b53659364d7c4cde99693688100'/>
<id>urn:sha1:463ff1f3d2d97b53659364d7c4cde99693688100</id>
<content type='text'>
SKILL.md leads with the capture routes ranked by evidence: OpenOCD RTT
streaming through the rtt skill's tools/rtt.py (--channel 1
--reset-before-attach, since the Init record that carries the timestamp
frequency and sync preamble is emitted once at boot; 238 KB captured without
it decodes to zero events), the live GUI recorder under Xvfb for depth, and
--from-raw post-mortem decode last. boards.md holds the per-board reference:
probe routes, RAM bases, measured buffer sizes, the full-pool campaign table
and the honest capture status of every board tried, including the gaps:
nrf5340dk HardFaults in vTaskStartScheduler before any task runs; PIO-USB
host ISRs are invisible to level-1 coverage (fruit_jam); metro_m4_express is
attach-only (the SAMD5x DSU CPU Reset Extension holds the core after an
in-session reset run); WCH is dump-only - the QingKe SDI has no autonomous
memory access, so a persistent debug session corrupts the running core ~1.9 s
into USB traffic, ruled out against lock, post-mortem mode and firmware; a
stray TU_ASSERT still halts for inspection rather than being silenced.

sysview_record.py drives the capture end to end and refuses to back up its
own ini stub as the user's SystemView config (a SIGKILLed run had consumed
the real backup that way). sysview_report.py turns the exported CSV into
the JSON report: ISR load, function pairing and durations, workload-window
CPU load from the scheduling events. Returns are classified on the bare
'Returns' detail - SystemView annotates a duration on only ~0.5% of them
(measured on stm32f407disco and raspberry_pi_pico), and when the annotation
is absent the duration comes from the recorded timestamps, verified the
same quantity at 167.998 vs a declared 168.000 ticks/us over 264 annotated
returns. Both loss markers count as overflow: explicit '*** Overflow ***'
rows and 'Returns to *** OVERFLOW ***' exit contexts - only counting the
first understated real loss by two orders of magnitude.
</content>
</entry>
<entry>
<title>pico2_etm_trace: RP2350 board on the MIPI-20 ETM trace carrier</title>
<updated>2026-08-28T18:08:31Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T09:39:58Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=6e8e2caf7fc2dc5575f5e0c71e69c1b19b3f5699'/>
<id>urn:sha1:6e8e2caf7fc2dc5575f5e0c71e69c1b19b3f5699</id>
<content type='text'>
Board files for the trace carrier (console GP12/13, LED GP10, I2C GP8/9,
PIO-USB host on GP20, all retargeted in board.cmake so the SDK defaults
cannot mux a trace pin), compile-time trace pin-conflict checks, the
measured DBGPAUSE rationale, Ozone project, and skill/docs updates.
Trace validated at the stock 150 MHz (75 MHz TRACECLK, +1 ns sampling):
zero overflow through a 15 s throughput soak; V2 probe ceiling 120 MHz.
</content>
</entry>
<entry>
<title>test/hil: drop the sysfs blindness subsystem and derive the recovery reserve</title>
<updated>2026-08-28T07:18:41Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T08:26:43Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=b190840e1d85f806575f58635827502d714febcf'/>
<id>urn:sha1:b190840e1d85f806575f58635827502d714febcf</id>
<content type='text'>
Two layers whose cost was a contract to reason about rather than an outcome.

SYSFS_UNKNOWN was a three-valued return five files had to keep apart, and
misreading unknown as absence was silent: a healthy board reported as a firmware
regression. What it guarded is real -- `serial` is served by usb_string_attr,
which takes usb_lock_device_interruptible (v6.12.96 sysfs.c:141-143), the same
lock a wedged usbfs ioctl holds -- so the BOUND stays, on every caller by
default. usb_scan reads `serial` on every device matching the VID, and hil_lock's
controller_of does that on essentially every board, so one wedged DUT would
otherwise stall every worker, not one. What goes is the third value.

read_sysfs now returns str or None, and the question the third value existed to
answer is asked directly instead, by two predicates that say which question they
answer: sysfs_stranded() is process-wide and sticky, for hil_pool_check's footer
("could anything here be the tool losing sight of healthy hardware?"), and
path_stranded(path) is per-device, which is what usbtest needs to tell a DUT
whose `serial` is held under device_lock from one that genuinely left the bus --
that difference decides whether it performs driver-registry writes that take the
uninterruptible device_lock.

Gone: _SysfsUnknown, SYSFS_UNKNOWN, sysfs_blind, sysfs_blind_note,
note_sysfs_strand, the cross-process blindness publishing and its report banner,
usb_scan's (list, bool) return, usbtest's inconclusive abort, _blind_note's slot
in the result tuple, and bounded_open, whose last caller went in the previous
commit.

The strand memo is rewritten around the one invariant that makes it safe to
reuse: it is keyed by the path's kernfs inode, captured BEFORE the read. A
busport does not change when a board returns to the same physical port, so a
path-only blacklist outlives the wedge and hil_pool_check's own recovery flow --
reset, reflash, wait_device polling that busport -- would never look at the
board again. A re-enumeration destroys the kernfs node and makes a new one, so a
changed inode is the all-clear. Two ceilings bound different things: per path
(_PATH_STRAND_MAX) for a board that flaps while still wedged, and per process
(_STRAND_MAX) as a backstop against RLIMIT_NOFILE, counted per PATH rather than
per reader because hil_pool_check runs four poll threads over one bus.

A board the pool guard never reached is now reported as run-aborted rather than
pool-timed-out, and outranks a stale board-locked cell for the same reason the
pool-timeout cell does.

Both predicates answer conservatively where they are consulted before something
irreversible. path_stranded() covers the paths read_sysfs answered None for
WITHOUT reading -- past _STRAND_MAX it declines to start another reader, and
vouching for a path nobody looked at hands usbtest's fail-CLOSED guard a
fabricated all-clear, running remove_id/unbind against a wedged device. usbtest's
startup lookup carries the same caveat hil_test's absent arm already did, because
its stderr is relayed verbatim into the report cell.

strand_note() survives the removal for the same reason master had it: every
caller that can say "not found" needs the same sentence, and the one site left to
re-invent it got missed -- a wedged-but-enumerated printer was reported as an
enumeration failure, sending a maintainer after firmware.

The two predicates are not interchangeable, and usbtest needs both. Its per-case
verdict is per-DUT -- a peer that stranded at case 2 must not make our board
report wedged at case 29 -- but the finally block's cleanup is process-wide:
remove_id plus an unbind of EVERY interface under the driver, including that
peer's, each taking the uninterruptible device_lock. So the verdict uses
path_stranded() and the global cleanup stays gated on sysfs_stranded().

USBTEST_RECOVERY_BUDGET was an independent 250s that could not actually contain
the ladder it reserved for, which is why usbtest.py carried a _time_left() gate
re-deciding before every step -- with a bare '- 35' for costs paid downstream
that nobody could re-derive. Between them the two produced a recovery that
skipped its own steps for most real hangs.

The reserve is now derived from the bounds usbtest itself declares, per flasher
and per target: a probe reset, a reflash, and the Rescue-DP POR plus retry a
wedged RP DAP needs, plus the settles and hil_util.REAP_GRACE for each bounded
step. The Rescue-DP legs are openocd-only and gated on the RP target cfg, and
a stub reset is screened out, so the reserve tracks each board's real ladder
instead of one fleet number: 390s for the two RP boards -- whose ladder the old
250 could not contain, which is exactly why the gates skipped their steps --
190s for the other seventeen probe-reset boards, and 150s for esptool and
lm4flash, whose reset is a no-op. Changing a bound in usbtest moves the
reserve with it, and a unit test asserts it covers the ladder.

With the room actually reserved, the child runs the ladder straight through:
recovery_steps, _time_left, the three per-step gates and the parsed-but-unused
--outer-timeout are gone. What stays is what decides outcomes -- the convoy_safe
gate, reset-before-reflash, the no_op screen so a stub that resets nothing is not
claimed, and wedged_pids() as the arbiter, because a clean flash only proves the
probe wrote the MCU.

hil_util.py 616 -&gt; 514 lines.
</content>
</entry>
<entry>
<title>Add RTT console/capture tooling (tools/rtt.py), rtt skill, and HIL harness support (#3853)</title>
<updated>2026-08-28T07:16:02Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T07:16:02Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=eca6caf673452c8ec940e2acf5e46d0631fb72bf'/>
<id>urn:sha1:eca6caf673452c8ec940e2acf5e46d0631fb72bf</id>
<content type='text'>
Promote SEGGER RTT from an inline debugging technique to a standalone skill
backed by one stdlib-only implementation in tools/rtt.py: a CLI and importable
module for console/capture over J-Link (RTTTelnetPort) and OpenOCD (rtt server)
probes, with probe selection by serial or VID:PID, control-block address via
--elf or --addr, bidirectional console, post-mortem ring dump, and
--reset-before-attach for boot-time capture. The HIL harness reads a board's
console over RTT when its probe has no VCOM ("logger": "rtt" plus a LOGGER=rtt
variant define), covering device_info, pool-check aliveness, and CI wiring.
Validated on 22 boards across both backends; 26 unit tests run in pre-commit.</content>
</entry>
<entry>
<title>Merge pull request #3863 from hathach/claude/validate-loop</title>
<updated>2026-08-28T07:10:33Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T07:10:33Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=4c7bb3ced98cb7262dd8eefd6c9f7df6e42ffcee'/>
<id>urn:sha1:4c7bb3ced98cb7262dd8eefd6c9f7df6e42ffcee</id>
<content type='text'>
validate workflow: loop validate -&gt; fix cycles until green

Turn the single-pass validate gate into a loop: run unit + builds +
size + PVS + claude/codex reviews in parallel; on a red verdict one
fix agent repairs the gate-failing evidence (CONFIRMED findings,
codex P0/P1, failed stages - PLAUSIBLE/quality stay report-only),
commits, and the affected stages re-run, up to maxCycles (default 5).

Hardened per review: fix-commit paths verified from git rather than
self-report, restartRequired when a fix edits the workflow itself,
per-stage evidence budgeting so the fixer prompt JSON never truncates
mid-document, dirty-tree and moving-base-ref guards, dead stage
agents retried instead of ending the loop, and only pure-docs fixes
skip a full stage re-run.</content>
</entry>
<entry>
<title>Merge pull request #3858 from hathach/claude/validator-done-signals</title>
<updated>2026-08-28T04:41:43Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T04:41:43Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=d2b4d8994eb2a1b8994601069f5180a9c979ee8c'/>
<id>urn:sha1:d2b4d8994eb2a1b8994601069f5180a9c979ee8c</id>
<content type='text'>
pr-review-validator: done waits for every auto-reviewer to settle</content>
</entry>
<entry>
<title>pr-review-validator: done waits for every auto-reviewer to settle on the head SHA</title>
<updated>2026-08-28T04:28:15Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T04:28:15Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=e413f05ee6eb6062a8b82c48ed685f0414234497'/>
<id>urn:sha1:e413f05ee6eb6062a8b82c48ed685f0414234497</id>
<content type='text'>
A cycle running before the bots posted saw zero findings and reported done;
with a fast-green CI the babysit loop could exit unreviewed. done now needs
every reviewer settled for the current head: Copilot's verdict review
(commit_id), Codex's verdict comment (Reviewed-commit line), its thumbs-up
reaction on the PR body, or the named claude-review check run — with quota/
error notices and the reaction freshness-gated on push time (check-suite
creation, not committer date) and every lookup paginated. pr-babysit re-arms
with backoff on a pending reviewer instead of exiting unactionable, skipping
the pointless final-cycle wait.
</content>
</entry>
<entry>
<title>pr-babysit: never edit HIL rig configs without user approval</title>
<updated>2026-08-27T17:16:26Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T17:14:28Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=09164de529cdde8aa4f454361a8dcd413bf43b32'/>
<id>urn:sha1:09164de529cdde8aa4f454361a8dcd413bf43b32</id>
<content type='text'>
The workflow's fix lane once skipped two host tests in test/hil/tinyusb.json
to green a check whose root cause was a failing fixture drive (reverted in
4b11d59a4). Rig rosters describe physical hardware: papering over a fixture
fault hides it from the user who has to swap the part. Now fixAndVerify
strips test/hil/*.json from every fix scope (a group left with no other
files is withheld and logged), the code-writer prompt carries the
constraint, and ok=false keeps such cycles from pushing. HIL stays red
when the fix is a hardware swap - that red is the signal.
</content>
</entry>
</feed>
