# Custom Chips — Velxio Developer Guide Velxio supports **user-defined chips** written in C and compiled to WebAssembly. Drop a chip on the canvas, wire its pins like any other component, and your chip runs alongside the simulated Arduino sketch. This is Velxio's equivalent of Wokwi's Custom Chips API — but with a clean-room header (`velxio-chip.h`), our own runtime, and zero code from third-party simulators. The toolchain (clang + WASI-SDK) is 100 % open source. --- ## Table of contents - [What you get](#what-you-get) - [The 30-second example: an inverter](#the-30-second-example-an-inverter) - [Anatomy of a chip](#anatomy-of-a-chip) - [The 11 example chips](#the-11-example-chips) - [Where to read next](#where-to-read-next) --- ## What you get A custom chip can: | Feature | Use it for | |---|---| | **Digital GPIO** (`vx_pin_*`) | Logic gates, level translators, level-triggered effects | | **Pin watch with edge detection** (`vx_pin_watch`) | Reactive chips: counters, debouncers, edge-triggered FSMs | | **Initialized output state** (`VX_OUTPUT_LOW`/`HIGH`) | Pin defaults to a known value at chip boot — no glitch | | **Analog out (DAC)** (`vx_pin_dac_write`) | Programmable voltage references, function generators | | **I2C slave** (`vx_i2c_attach`) | EEPROMs, RTCs, IO expanders, sensors | | **SPI slave** (`vx_spi_attach`/`start`/`stop`) | Shift registers, ADCs, displays, flash chips | | **UART** (`vx_uart_attach`/`vx_uart_write`) | GPS modules, BT/WiFi modems, anything that talks Serial | | **User-editable attributes** (`vx_attr_*`) | Knobs the user tweaks: gain, threshold, baud, etc. | | **Timers** (`vx_timer_*`) | Periodic events, oscillators, watchdogs | | **Framebuffer / display** (`vx_framebuffer_init`/`vx_buffer_write`) | Custom LCDs, OLEDs, dot-matrix displays | | **Logging** (`vx_log` and `printf`) | Debug output to the chip console | Every API call is documented in [`wiki/custom-chips-api-reference.md`](./wiki/custom-chips-api-reference.md). --- ## The 30-second example: an inverter A chip with one input and one output. `OUT` is always the inverse of `IN`. ### `inverter.c` ```c #include "velxio-chip.h" #include typedef struct { vx_pin in; vx_pin out; } chip_state_t; static void on_in_change(void *ud, vx_pin pin, int value) { chip_state_t *s = (chip_state_t*)ud; vx_pin_write(s->out, value ? VX_LOW : VX_HIGH); } void chip_setup(void) { chip_state_t *s = (chip_state_t*)malloc(sizeof(chip_state_t)); s->in = vx_pin_register("IN", VX_INPUT); s->out = vx_pin_register("OUT", VX_OUTPUT); vx_pin_write(s->out, vx_pin_read(s->in) ? VX_LOW : VX_HIGH); vx_pin_watch(s->in, VX_EDGE_BOTH, on_in_change, s); vx_log("inverter ready"); } ``` ### `inverter.chip.json` ```json { "schema": "velxio-chip/v1", "name": "Inverter", "author": "you", "license": "MIT", "description": "OUT = !IN.", "pins": ["IN", "OUT", "GND", "VCC"], "attributes": [] } ``` ### Try it in the editor 1. Open Velxio. Click **Add Component** → search "Custom Chip" → select. 2. The Custom Chip Designer opens. Switch to the **Examples** tab. 3. Click **Inverter** — code loads in the editor. 4. Click **Compile** → "✓ Compiled — 61.8 KB". 5. Click **Save & Place** — the chip appears on the canvas. 6. Wire `IN` to your Arduino's pin 13 and `OUT` to a LED. 7. Run the blink sketch → the LED toggles inverse to the built-in LED. --- ## Anatomy of a chip A chip is **two files**: ``` mychip.c // C source — the logic of the chip mychip.chip.json // Metadata — pin layout, attributes, optional display ``` ### Lifecycle The chip exports exactly **one** function: ```c void chip_setup(void); ``` Velxio calls `chip_setup()` once per chip instance after the simulation starts. Inside it the chip: 1. **Allocates state** — typically one `malloc(sizeof(chip_state_t))` per instance. 2. **Registers pins** with `vx_pin_register(name, mode)`. 3. **Attaches peripherals** if needed: `vx_i2c_attach`, `vx_uart_attach`, `vx_spi_attach`. 4. **Subscribes to events** with `vx_pin_watch` and/or `vx_timer_create` + `vx_timer_start`. 5. Returns. **No event loop.** The chip is purely reactive. After setup, the chip runs **only** inside callbacks the host invokes: - A pin watch fires → your callback runs → maybe writes other pins. - The I2C bus addresses your slave → your `on_connect`/`read`/`write`/`stop` runs. - A timer expires → your callback runs. - A UART byte arrives → your `on_rx_byte` runs. This means your chip never blocks, never loops forever, and uses zero CPU between events. ### State per instance If the user drops two of your chips on the canvas, they each get their own `WebAssembly.Instance` with **separate memory**. Inside `chip_setup()` you `malloc` a new state struct — there's no shared global state to worry about. ### `chip.json` schema ```json { "schema": "velxio-chip/v1", "name": "Display name", "author": "Your name", "license": "MIT", "description": "Short text shown in the picker tooltip", "pins": [ "IN", // string — auto-laid out left/right "OUT", { "name": "SCL", "x": 0, "y": 24 }, // object — explicit position { "name": "SDA", "x": 0, "y": 36 } ], "attributes": [ { "name": "threshold", "type": "int", "default": 4, "min": 1, "max": 1024 }, { "name": "gain", "type": "float", "default": 1.0, "min": 0, "max": 10, "step": 0.1 } ], "display": { "width": 128, "height": 64 } // optional — for chips with a screen } ``` Empty strings in `pins` skip a slot (so your DIP layout matches a real chip). `attributes` show up as sliders/inputs in the chip's properties dialog. `display` enables the framebuffer API. --- ## The 11 example chips The Custom Chip designer ships a gallery with 11 ready-to-go examples, covering every protocol the runtime supports: | Chip | Protocol | What it shows | |---|---|---| | **Inverter** | GPIO + watch | Simplest possible chip — start here | | **XOR Gate** | GPIO + 2 inputs | Multiple watches, recompute output on any edge | | **CD4094** | GPIO state machine | Edge detection (RISING vs BOTH), multi-pin shift register | | **Pulse Counter** | GPIO + attributes | User-configurable threshold via slider | | **74HC595** | SPI slave | SPI transfer with re-arm pattern | | **MCP3008** | SPI + analog | SPI ADC: read voltage, return 10-bit result | | **24C01 EEPROM** | I2C slave | Tiny memory device with pointer auto-increment | | **24LC256 EEPROM** | I2C slave | 16-bit addressing + page writes | | **PCF8574** | I2C IO expander | Reading/writing 8 pins atomically | | **DS3231 RTC** | I2C state | 19 registers, BCD encoding, register pointer | | **ROT13 UART** | UART | Receive a byte, transmit transformed byte | Each is fully explained in [`wiki/custom-chips-examples.md`](./wiki/custom-chips-examples.md). --- ## Where to read next | If you want to … | Read | |---|---| | Understand every host function in detail | [API reference](./wiki/custom-chips-api-reference.md) | | See the 11 chip examples worked through | [Examples walkthrough](./wiki/custom-chips-examples.md) | | Set up the toolchain or write tests | [Build & test guide](./wiki/custom-chips-build-and-test.md) | | Run custom chips on ESP32 (backend runtime architecture) | [ESP32 backend runtime](./wiki/custom-chips-esp32-backend-runtime.md) | | Know which boards support which protocols | [Board support matrix](../test/autosearch/07_multi_board_support.md) | ### Quick links to source - C SDK header — [`backend/sdk/velxio-chip.h`](../backend/sdk/velxio-chip.h) - Frontend runtime — [`frontend/src/simulation/customChips/`](../frontend/src/simulation/customChips/) - Example chips — [`test/test_custom_chips/sdk/examples/`](../test/test_custom_chips/sdk/examples/) - Backend compile service — [`backend/app/services/chip_compile.py`](../backend/app/services/chip_compile.py) - Sandbox test suite — [`test/test_custom_chips/`](../test/test_custom_chips/) --- ## Design philosophy A few decisions worth knowing: - **Reactive, not procedural.** Your chip never has a `loop()`. The host calls your callbacks; you do small bits of work and return. This guarantees the chip can't hang the simulator. - **Shared-nothing memory.** Each chip instance gets its own WASM linear memory. Two instances of the same chip can't accidentally share state — no globals to worry about. - **The C type system is your friend.** `velxio-chip.h` declares concrete types (`vx_pin`, `vx_attr`, `vx_i2c_config`) and uses `_Static_assert` to guarantee the struct layouts match between your chip and the host. If you add a field, the assertion fires and you fix the runtime accordingly. - **No dependency on Wokwi.** This is a clean-room implementation. The toolchain is `clang` + `wasi-sdk`, both Apache-2.0. Our header and runtime are Velxio-original.