- state.rs: ports of mattonella_state.py (569 L), _dac_setting.py and the RegisterField byte plumbing — full 0x6C register image (64 pixel bytes, globals 0x40-0x4B, pairwise finetune-DAC nibbles 0x4C-0x6B) with exact range checks and enum parsing (ChannelMode, CalibrationTime) - mattonella.rs: port of mattonella.py (426 L) — DACInterface (enable bit 7 + 7-bit value RMW), 20 class-level descriptors, pixel batch ops (power/frontend/adj/ctrl/mask/ftdac), calibrate, full-state read/write - 9 integration tests; verified from_bytes(to_bytes(s)) == s round-trip - fake data_server moved to tests/common and fixed to advance the subaddress on multi-byte reads/writes (consecutive registers) - divergences D1-D5 documented in docs/porting-notes.md - workspace: 30 tests green
innesco-rs
Rust clone of innesco (the IGNITE64 control/DAQ software, timespot/innesco
on baltig). This repo is a separate clean-room-style port: the original
Python/C repository is untouched, and its license/authorship is respected.
Deployment target: AMD Kria K26 SOM (Zynq UltraScale+ MPSoC, quad Cortex-A53, aarch64, Ubuntu 22.04) — the board buffering/timestamping the IGNITE64 detector data.
Layout
| crate | ported from | status |
|---|---|---|
innesco-protocol |
innesco/ecs/protocol.py |
ECS wire protocol (Read/Write/Ping, >BI headers) + TCP client |
innesco-decoding |
innesco/decoding/csrc/decoding.c |
13-byte frame decoder, DCO calibration, position map; golden tests |
innesco-cli |
— | ping, decode subcommands (zero-dependency) |
Zero external dependencies (pure std): cross-compiling for arm64 needs no
cross-linked C libraries, and release binaries are small (panic = "abort", LTO).
Build (x86_64 host)
cargo test --workspace # golden tests
cargo build --release # host binary
export CARGO_TARGET_DIR=/tmp/innesco-rs-target # keep artifacts off NFS
Build for arm64 (Kria K26)
scripts/build-arm64.sh --run-tests # static musl binary (recommended)
scripts/build-arm64.sh --gnu # dynamic, glibc (Ubuntu 22.04 on board)
The script bootstraps everything without root: rustup target add,
a ziglang pip venv as cross linker (via cargo-zigbuild), then
cargo zigbuild --release --target aarch64-unknown-linux-musl.
Artifacts land in dist/aarch64-<musl|gnu>/innesco-cli.
Deploy to the Kria: scp dist/aarch64-musl/innesco-cli <board>:~/ && ssh <board> ./innesco-cli ping localhost 8888.
Porting notes
See docs/porting-notes.md for the Python/C → Rust mapping and the
(deliberate) divergences from the C extension (64-bit arithmetic where the C
overflows 32-bit, strict socket reads where Python truncated silently,
skip-and-count instead of abort on malformed packets).
Open Points
(details agreed with Lucio 2026-11-12; user-editable — tell me what to focus on)
- Storage layer (after #3) — decisions taken: polars-rs for parquet
I/O (feature parity with the notebooks) + S3/MinIO upload is crucial
(no local storage on the board — data must be pushed out). Schema need not
be byte-identical, but must keep the notebook query surface: columns
address, ix, iy, quadrant, tot, adj_ctrl, period, integr_time, v_inj(grep of notebooks 02/04/05/06/07) and the file-path conventions (results/{date}/analog_power=*/dcos-*.parquet,dataset-tag-{n}.parquet— duckdb queries useSELECT *, so paths matter more than column order). Original: 185-lineParquetStoragethread (queue → parquet → S3). - REST API (after #3): 7 endpoints in
rest_api.py(/ping,/metrics,/state,PUT /chunksize,POST /tag,POST /daq,GET /dataset). Port: axum + tokio in a separate crate with feature flag (library stays dependency-free)./stateand/daqdepend on #3; the other four can be ported independently. - Board/slow-control model — IN PROGRESS, slices 1-2 done:
- ✅ slice 1:
_fields.py(199 L) +board.py(89) +pixel.py(77) →crates/innesco-ecs(7 integration tests against a fake data_server, incl. RMW semantics and the crystal-config sequencer; two faithful-port findings documented in docs/porting-notes.md #7/#8). - ✅ slice 2:
mattonella_state.py(569 L) +mattonella.py(426 L) +_dac_setting.py→state.rs(full 0x6C-byte register image with exactRegisterFieldserialization) +mattonella.rs(DACInterface, 20 mattonella descriptors, pixel batch ops, calibration, full-state I/O). 9 more tests; verified round-tripfrom_bytes(to_bytes(s)) == s. - next: (3) asic_state 626 L → (4) quadrant 502 L → (5) daq+DataProcessor 574 L. Mirror-1:1 first, type-state validation later.
- ✅ slice 1:
- Real-board validation: no qemu here, no network to the boards.
(a) A raw frame dump from a real acquisition (even small) → decoder
validated on physical data as an official golden test — highest value,
zero prerequisites. Lucio will try next week; reminder set for
Wed 2026-11-18 09:00. (b) network access to a Kria running
data_server→ liveinnesco-cli ping+ harmless I2C read (0x7E). - CI runner — deprioritized (Lucio 2026-11-12): verified 2026-11-12
that no Actions runner exists on this Gitea and self-hosting is impossible
here (no Docker daemon). Local
scripts/build-arm64.sh --run-testsis the working CI substitute.