Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ jobs:
with:
submodules: recursive

# The ALSA headers, so the Linux legs build async's hardware bridge
# (examples/alsa_bridge.cpp, the roadmap's A11) and run its checks.
- name: Install ALSA headers
if: runner.os == 'Linux'
run: sudo apt-get update -q && sudo apt-get install -y -q libasound2-dev

- name: Configure
env:
CC: ${{ matrix.cc }}
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ jobs:
with:
submodules: recursive
- name: Install tools
run: sudo apt-get update && sudo apt-get install -y clang-tidy-18 cmake python3
# Every engine with its tests and examples, plus every engine's icount
run: sudo apt-get update && sudo apt-get install -y clang-tidy-18 cmake python3 libasound2-dev
# Every engine with its tests and examples (libasound2-dev brings async's
# ALSA bridge in, the roadmap's A11), plus every engine's icount
# workloads (the one family option builds them all). The async workloads
# joined the gate with the roadmap's A2; the comparison workloads
# (cmp_main.cpp, TAP_SR_ICOUNT_COMPARE) are not configured here.
Expand Down
47 changes: 39 additions & 8 deletions async/docs/HARDWARE_TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,42 @@ stream to a file and analyze it offline with the notebook tooling
machinery) — the clocks are real even if the signal never goes analog.

[`examples/alsa_bridge.cpp`](../examples/alsa_bridge.cpp) (built as
`tap_sr_async_alsa_bridge` when ALSA is found) implements this harness: `--csv` logs
the per-second `status()` telemetry for plotting the ppm trace, `--dump`
captures the post-ASRC float stream for the offline analysis above, and
`--tone <hz>` substitutes a synthetic sine paced by the input device's real
clock when the analog path is not trusted.
`tap_sr_async_alsa_bridge` when ALSA is found) implements this harness:

- **`hw:` devices only.** `--in` and `--out` are required and must name
`hw:` devices; the bridge refuses `default`, `plughw:` and every other
plugin device before opening anything. On a Pi, `default` routes through
PipeWire and its own resampler, and the bridge would measure that
converter's clock instead of the dongle's crystal.
- **`--csv <path>`** writes the per-second record:
- the converter's `status()`;
- `CLOCK_MONOTONIC_RAW`;
- each device's cumulative frame count, from its thread;
- each device's ALSA xrun count (`-EPIPE`), kept apart from its other
recoveries;
- where the driver provides one, each device's last hardware pointer
update with its own timestamp (`snd_pcm_htimestamp`, on the same raw
clock), which carries no scheduling jitter.
- **[`examples/alsa_bridge_report.py`](../examples/alsa_bridge_report.py)
analyses that record.** It regresses each device's frames against time
to get the two crystals' rates and their true offset in ppm, from the
thread counts and from the hardware timestamps. It compares that offset
with the servo's mean while locked, and states the ppm wobble and the
FIFO's breathing (standard deviation and peak to peak) and the final
discontinuity counts. `--plot` draws the ppm and fill traces (matplotlib).
Its `--self-test` checks the arithmetic on a synthetic hour, as
`async.Examples.AlsaBridgeReportSelfTest`.
- **`--rt`** runs both audio threads `SCHED_FIFO` with every page locked
(`mlockall`), and fails rather than measuring without them.
- **`--dump`** captures the post-ASRC float stream for the offline analysis
above.
- **`--tone <hz>`** substitutes a synthetic sine paced by the input
device's real clock when the analog path is not trusted.

The Linux CI legs install `libasound2-dev`, so the bridge is built,
clang-tidy-clean and checked to refuse a plugin device
(`async.Examples.AlsaBridgeRefusesPluginDevices`) on every PR. It still
needs two dongles to run.

## Setup 2 — Pi (Cortex-A) + Raspberry Pi Pico 2: the M33 target on real silicon

Expand Down Expand Up @@ -101,9 +132,9 @@ emulation work predicted.

What exists and what remains:

- **Setup 1**: shipped — `examples/alsa_bridge.cpp` (see above). Still
missing: a small script to plot the `--csv` ppm trace and run the
notebook analysis over a `--dump` capture.
- **Setup 1**: shipped. `examples/alsa_bridge.cpp` and
`examples/alsa_bridge_report.py` are described above. Still missing: a
script that runs the notebook analysis over a `--dump` capture.
- **Setup 2**: shipped — `examples/pico2_cyccnt/` (cycle measurement) and
`examples/pico2_dualcore/` (dual-core deployment), both building
flashable UF2s; the measured numbers await a physical Pico 2.
Expand Down
16 changes: 16 additions & 0 deletions async/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ if(ALSA_FOUND)
tap_sr_async_warnings
ALSA::ALSA
Threads::Threads)
# No hardware on CI: the measurement's guard is what can be checked there.
# A plugin device must be refused before anything is opened.
add_test(NAME async.Examples.AlsaBridgeRefusesPluginDevices
COMMAND tap_sr_async_alsa_bridge --in default --out hw:0)
set_tests_properties(async.Examples.AlsaBridgeRefusesPluginDevices PROPERTIES
PASS_REGULAR_EXPRESSION "refusing 'default': only hw: devices"
LABELS async)
else()
message(STATUS "ALSA not found; skipping tap_sr_async_alsa_bridge example")
endif()

# The bench's analysis (alsa_bridge_report.py): its arithmetic against a
# synthetic run with known rates, wherever Python is.
find_package(Python3 COMPONENTS Interpreter QUIET)
if(Python3_Interpreter_FOUND)
add_test(NAME async.Examples.AlsaBridgeReportSelfTest
COMMAND Python3::Interpreter ${CMAKE_CURRENT_SOURCE_DIR}/alsa_bridge_report.py --self-test)
set_tests_properties(async.Examples.AlsaBridgeReportSelfTest PROPERTIES LABELS async)
endif()
Loading
Loading