4.1 KiB
Velxio custom-chip capabilities (as of 2026-04-29)
Sourced by reading the in-repo SDK and runtime; no external lookups needed.
Toolchain
-
C source → WASM via clang + WASI-SDK, driven by
backend/app/services/chip_compile.py. -
Build flags (from the same file):
clang --target=wasm32-unknown-wasip1 -O2 -nostartfiles -Wl,--import-memory -Wl,--export-table -Wl,--no-entry -Wl,--export=chip_setup -Wl,--allow-undefined -I sdk/ chip.c -o chip.wasm -
Single mandatory export:
void chip_setup(void). Everything else is reactive: pin watch callbacks, timer callbacks, I²C/SPI/UART callbacks. There is no host-driven main loop and nochip_loop()-style polling hook.
Memory per chip instance
From frontend/src/simulation/customChips/ChipRuntime.ts:170:
this.memory = new WebAssembly.Memory({ initial: 2, maximum: 16 });
- 2 pages × 64 KB = 128 KB initial.
- Up to 16 pages = 1 MB max per instance.
- Each chip instance gets its own linear memory — no shared globals between two instances of the same chip.
- WASI-SDK ships
malloc/free/memset/etc., so dynamic allocation for register files, decoder tables, and prefetch queues is fine.
Pin API (from backend/sdk/velxio-chip.h)
vx_pin_t vx_pin_register(const char* name, vx_pin_dir dir);vx_pin_read,vx_pin_write,vx_pin_set_dir— for digital I/O.vx_pin_watch(pin, edge, callback, user_data)— edge can beVX_RISING,VX_FALLING, orVX_BOTH.vx_pin_dac_write/vx_pin_read_analog— analog (not needed for these CPUs but available).
There is no fixed pin-count cap. The SDK uses a dynamic PinEntry[]
table. The 40-pin DIP CPUs in scope (8080 / Z80 / 8086) are well within
this.
Time and clocks
uint64_t vx_sim_now_nanos(void)— simulated time, monotonic.vx_timer_t vx_timer_create(void);vx_timer_start(timer, period_nanos, repeating, callback, user_data)vx_timer_stop(timer)
This is the mechanism we will lean on hardest: a CPU emulator schedules a repeating timer at the chip's clock period (e.g. 250 ns for 4 MHz Z80) and the callback executes one instruction (or one machine cycle) per firing.
I/O helpers we will not use
I²C, SPI, UART, framebuffer helpers exist (see
docs/wiki/custom-chips-api-reference.md) but they are higher-level
slave/master abstractions. CPUs drive raw pins (RD, WR, MREQ, IORQ,
ALE, etc.), so we will register those as plain GPIOs and bit-bang the
bus protocol from the chip itself.
Existing precedent in the repo
Eleven example chips live in test/test_custom_chips/sdk/examples/.
Closest relatives to a CPU:
sn74hc595.c— proves a multi-step, clocked, stateful protocol (SPI shift-register + latch) works under the reactive callback model.ds3231.c— proves persistent register files (19 BCD registers with auto-increment) work.pulse-counter.c— proves a chip can react to many edges per second without dropping events.
No full CPU emulator exists yet in the repo; this folder is the first.
Hard limits that matter for CPUs
| Limit | Value | Comfortable for… |
|---|---|---|
| Linear memory | 1 MB / instance | Even a 64 KB Z80 RAM model + decoder tables fits, but external RAM should still live in a separate chip. |
| Pin count | No hard cap | 40-pin packages are fine. |
| Timer resolution | Nanoseconds | Up to ~10 MHz instruction rates are fine; sub-ns is not. |
| WASM execution budget | Set by host loop | Need to keep per-callback work bounded — no infinite spin. |
What is not available
- No direct shared-memory bus between chips. Two chips talk only via pin transitions on wires.
- No interrupt controller primitive — interrupts are just pin edges the CPU watches and reacts to (which is how the real silicon works anyway).
- No SPICE / analog simulation guarantees timing accuracy at the nanosecond level under load — we will treat the simulated nanosecond clock as the source of truth, not wall time.