Start here when working on _core, the shared firmware and tooling repository
for Zeptocore, Zeptoboard, Ectocore, and Ezeptocore. Run the commands below from
the repository root. Source and artifact paths written in the development notes
also refer to the repository root; Markdown links are relative to each document.
The short orientation note is readme-starthere.md.
SDK upgrade and validation: Pico SDK 2.3.1 and connected hardware validation.
Current performance controls: Zeptocore A/B knob mappings.
- Use the repository map and build instructions below to locate the relevant code.
- Read seek-map design for allocation maps, persistence, memory limits, and filesystem ownership.
- Read implementation status and hardware results for the original validation record, together with the subsequent review notes below.
- Use diagnostics when investigating runtime behavior, then follow the topic-specific documents in the index.
- Consult the implementation brief for the original requirements and acceptance criteria. It is a historical implementation brief, including session-specific hardware and completion statements.
| Location | Role |
|---|---|
| main.c, lib/includes.h, lib/globals.h | Firmware startup, module integration, and shared state. |
| lib/ | Device wrappers, controls, audio rendering, DSP, media lifecycle, and drivers. Many implementations live in headers. |
| lib/audio_callback.h, lib/realtime_stretch.h | Normal playback and time-stretch rendering. |
| lib/audio_seek_map.c, lib/audio_media.h, lib/audio_media_control.h, lib/audio_media_owner.c | Persistent map/cache management, playback file lifecycle, foreground integration, and cross-core filesystem ownership. |
| lib/sdio/ | Bundled FatFs and SD support; allocation validation and mapped seeking are implemented in ff.c. |
| lib/my_pico_audio/, lib/my_pico_audio_i2s/ | Audio buffers, format conversion, I2S/DMA output, and restart queue integration. |
| core/ | Go application and sample-preparation/web-server tooling. |
| dev/ | Development utilities, audio/header generators, and device tools. |
| scripts/ | Host-side build comparisons, diagnostics, hardware workloads, and capture analysis. |
| test/ | Native firmware tests and Python protocol tests. Older standalone module tests also live in lib/test/. |
Makefile, CMakeLists.txt, lib/cmake *_compile_definitions*.cmake |
Build targets, feature switches, and device/buffer configurations. |
| schematics/ | Hardware schematics. |
| docs/ | Development notes and legacy Hugo site sources/assets. The site's public/ output is generated; top-level development notes are ordinary Markdown. |
The standard Zeptocore build is:
make zeptocoreIt produces zeptocore.uf2 and build/_core.elf, using the 441-frame device
configuration. make zeptocore_256 selects the supported 256-frame configuration.
Other device targets are listed in the Makefile. These targets
share build/ and lib/cmake/target_compile_definitions.cmake; use separate CMake build
directories and CORE_COMPILE_DEFINITIONS for simultaneous configurations, as
shown in the diagnostics build instructions.
The Makefile downloads Pico SDK 2.3.1 and Pico Extras sdk-2.3.1, creates a stable
Python 3.14 .venv with uv, and generates DSP/audio headers when absent. Host tools
include CMake, Make, ARM GCC with newlib, Git, uv, clang-format, Go, and SoX.
The firmware CI workflow documents its Linux
setup, including the TinyUSB patch used by CI. Building firmware does not flash it.
To set up the Python tools directly:
uv self update
uv venv .venv --python 3.14.8
uv pip install --python .venv/bin/python -r requirements.txt
source .venv/bin/activateThe pinned dependencies also support Python 3.11 for the firmware CI jobs.
Use a current uv so its download catalog includes stable Python 3.14 builds.
If uv was installed through a package manager, update it through that manager
instead of uv self update. The exact patch version excludes early 3.14 alpha
builds that a bare -p 3.14 can reuse; those builds cannot load the current NumPy wheels.
Check .venv/bin/python -VV for the interpreter version. Recreate an environment
using an alpha, beta, or release candidate with uv venv .venv --python 3.14.8 --clear,
then install the requirements again. Reinstalling packages alone does not replace
its interpreter. For an existing stable Python 3.14 environment, rerun the install
command to update the packages.
Make checks existing SDK/extras checkouts against the pinned tags and initialized
submodules. It reports a mismatch without overwriting local changes. For an older
installation, move the old SDK, extras, and build directories into a uniquely
named backup under artifacts/, then run make zeptocore to fetch the pinned
dependencies and configure a fresh build. Direct CMake builds can select other
SDK/extras paths for comparisons; use a separate build directory for each pair.
Run the relevant native suites after building the generated headers:
.venv/bin/python test/fatfs_seek/run.py
.venv/bin/python test/seek_diagnostics/run.py
.venv/bin/python test/audio_source/run.py
.venv/bin/python test/dsp_loops/run.py
.venv/bin/python test/midi/run.py
.venv/bin/python test/rack/run.py --sanitize address,undefinedThese runners invoke host cc/c++ with sanitizers. The FatFs runner uses GNU
linker --wrap=calloc, which Apple's linker does not support; the diagnostics
runner also encounters Mach-O section/Clang compatibility issues on macOS.
A successful macOS firmware build does not establish that these native suites
pass unchanged. The earlier review used temporary host-portability adaptations
for some tests; those adaptations are not part of the repository.
On 2026-09-16, make zeptocore passed on macOS at c5f884b (PR #830 merged),
with three conversion warnings in lib/mcp23017/mcp23017_lib.c. No hardware
flashing or playback validation was performed during that build.
On each Linux server checkout, add this line to root's crontab (sudo crontab -e):
* * * * * /www/ezeptocore/dev/auto_update.pyReplace /www/ezeptocore with that checkout's absolute path. The executable
auto_update.py locates the repository from its own path,
so cron needs no cd or arguments. The repository folder name selects the
service: ezeptocore restarts zns.ezeptocore.service, ectocore restarts
zns.ectocore.service, and zeptocore restarts zns.zeptocore.service.
For a persistent log, use this entry instead and include the log in the server's normal log rotation:
* * * * * /www/ezeptocore/dev/auto_update.py >> /var/log/ezeptocore-update.log 2>&1The cron account needs noninteractive Git access, write access to the checkout,
and permission to restart its existing systemd service. Install Python 3.8+,
Git, Make, Go, Hugo, uv, and the server's existing build dependencies first.
The cleanup utility requires Python 3.11+ and its inline click and rich
dependencies, which uv manages. The script extends cron's PATH with standard
system directories, /usr/local/go/bin, and the account's .local/bin, bin,
go/bin, node/bin, and .cargo/bin. It does not load interactive shell
configuration; custom tool locations must be included in cron's PATH.
Each invocation fetches the checked-out branch's configured upstream and fast-forwards to the fetched commit. Detached checkouts, missing upstreams, local commits ahead of upstream, divergent history, and conflicting local edits stop deployment with an error. Git preserves nonconflicting local edits; those edits remain part of the build. No reset or stash is performed. Git hooks are disabled for the script's commands so older post-merge hooks cannot trigger duplicate builds or restarts.
After an update, the script runs make core_server, including the Hugo docs
build, then restarts the service and checks systemctl is-active. A failed
build does not trigger a restart. Build and restart failures stay pending and
retry on the next invocation, even if Git is already up to date. Deployment
requires a successful fetch. The first invocation assumes an existing
core_server already matches the current commit; a missing binary triggers a
build. A successful immediate service check does not monitor later crashes or
provide automatic rollback.
Cleanup runs on the first invocation and then once every 24 hours. It calls
delete_old_folders.py with --delete --yes for:
| Directory relative to the checkout | Retention |
|---|---|
drum_separation_model_output/modelo_final |
30 days |
storage |
60 days |
The existing utility deletes immediate, nonhidden child folders only when their newest file anywhere inside is older than the retention period. Empty folders, folders with no files, and files directly inside the target directory are left alone. Missing target directories are skipped. Both cleanup commands are attempted even if one fails, and a deployment error does not prevent cleanup. The daily timestamp records an attempt, including failed or interrupted scans, so another attempt waits 24 hours.
The lock and state live under the checkout's Git metadata directory, normally
.git/auto-update/: lock, deployment.json, and cleanup-attempt.json.
Deployment records the last successful commit and whether work remains pending;
state files are replaced atomically. Overlapping invocations exit quietly.
Unchanged checks are quiet; actions and command output have timestamps, and
command failures return a nonzero exit status. The script never installs cron
entries itself. Use dev/auto_update.py --help to read usage without running it.
Run the isolated regression tests with:
python3 -m unittest discover -s test/auto_update -vTests use temporary Git repositories and stub build/service commands. If uv
is available, they also exercise the real cleanup utility on temporary data.
The fast-seeking work was merged in PR #830. Maps describe contiguous runs of
filesystem clusters; they do not cache audio data. Startup validates saved
allocation chains, rebuilds missing/changed maps, and persists recoverable index
generations. Every open must attach a current-mount map because FatFs clears
FIL.cltbl when opening a file.
- Limits are 256 indexed physical WAVs, two RAM map entries, and 64 DWORDs (31 contiguous runs) per map. Excess files or fragmentation use ordinary seeks.
- File duration alone does not determine map size. Allocation validation still walks filesystem metadata on each mount, so long files can increase startup work.
- The live file's map is pinned. A runtime cache miss uses ordinary seeking and queues work until playback is stopped/muted, effect tails are disabled, bass voices are idle, and filesystem ownership has been acknowledged.
- The audio core owns filesystem access during playback. Foreground filesystem work uses the shared ownership handoff; optional mapping does not justify an uncoordinated SD operation during rendering.
- Normal builds enable seek-map attachment. Diagnostics, detailed timing, test controls/fixtures, next-file preparation, and extra output buffering default off.
- Clock restart correction and wake default on for the Ectocore/Ezeptocore path; immediate wake is limited to the 256-frame core-1 worker configuration.
- Variable splicing uses each selected slice's duration on the 192 PPQN timer, including between external 2 PPQN clock edges. Clock input supplies tempo and restart synchronization; the fixed splice grid does not determine variable slice timing. TAP+MODE preserves its nearest-clock-edge reset behavior. The shared-engine tests cover uneven slices, source formats, resets and clock loss.
- The same series also fixes the pitch/retrigger source-buffer overflow and optimizes delay/filter processing. See the documents below for each change.
The constants and feature flags are defined in audio_seek_map.h, CMakeLists.txt, and audio_restart.h.
The following findings from the follow-up review remain present at c5f884b.
They qualify the original documents' completion claims; no fixes were applied
during this documentation consolidation.
- In audio_seek_map.c,
identity()hashesfs->bitbasefor every filesystem, although FatFs initializes that field for exFAT. The native FAT remount test reproduced unnecessary map rebuilds/index writes when the unused field differed. Zero-initialized startup storage can mask this. - In ff.c,
ff_clmt_inspect()checksobj.n_fragandobj.statwithout restricting the exFAT allocation-state checks to exFAT. The native benchmark reproduced rejection of an otherwise valid FAT file. - The optional 3784-byte map arena is allocated after reverb initialization but before later mandatory DSP/control allocations. The review identified missing heap-reserve protection as a risk; a resulting hardware failure was not reproduced.
Temporary copies with the two FAT/exFAT guards passed the FatFs suite after the host-portability adaptations mentioned above. That result does not mean the unmodified checkout passed every native suite or that hardware was retested.
All 14 Markdown documents introduced by PR #830 are collected directly in this folder, including the relocated implementation brief. Keep detailed evidence in the topic documents and use this index for orientation.
| Document | Purpose |
|---|---|
| MIDI slice triggering | Channel 1 note mapping, transport/mute behavior, Ableton routing, and native tests. |
| Implementation brief | Original scope, requirements, integration checklist, and acceptance/completion record. |
| Seek-map design | Memory policy, durable index, allocation validation, filesystem ownership, and mapped execution. |
| Implementation status | Original integration audit, correctness coverage, hardware evidence, and limitations. |
| Hardware results | Zeptocore map-on/off comparisons, persistence checks, memory use, and supported buffer sizes. |
| Diagnostics usage | Build/run instructions, SWD service, captures, metrics, and interpretation. |
| Diagnostics plan | Original measurement protocol and instrumentation-overhead acceptance gate. |
| Ectocore hardware | Device-specific validation and follow-up measurements. |
| Detailed timing | Callback-stage profiling and remaining starvation cases. |
| Freeze fix | Retrigger/pitch stack overflow, bounded source buffer, and playback-rate limits. |
| DSP optimization | Exact fixed-point multiplication and delay/filter loop optimizations. |
| Clock latency | Measurements before the clock restart correction. |
| Clock restart fix | Phase correction, fade, immediate renderer wake, and measured results. |
| Next-file experiment | Staged file preparation experiment; disabled by default. |
| Output-buffer experiment | Additional buffering and its latency tradeoff; disabled by default. |
Hardware capture and firmware paths under artifacts/seek/ are historical
evidence references. artifacts/ is ignored by Git and may be absent in a fresh
checkout. Verify the relevant build, device, and available evidence before
reusing a measurement or assuming a previously described hardware setup.
Older references include the Ectocore quickstart and Zeptocore specification. Product guides, downloads, and licensing are linked from the root README.