105 lines
4.1 KiB
Markdown
105 lines
4.1 KiB
Markdown
|
|
# 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 no
|
|||
|
|
`chip_loop()`-style polling hook.
|
|||
|
|
|
|||
|
|
## Memory per chip instance
|
|||
|
|
|
|||
|
|
From `frontend/src/simulation/customChips/ChipRuntime.ts:170`:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
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 be
|
|||
|
|
`VX_RISING`, `VX_FALLING`, or `VX_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.
|