Component Model
Primitives, composites, and the full primitive reference
Try a Primitive
Search for any primitive, see its ports and parameters, and interact with a live demo:
The Core Invariant
Circuits are either structural (nodes + connections) or behavioral (eval/onTick functions). Structural circuits have zero runtime overhead — they're elaborated away before simulation.
When you write a composite like HalfAdder using nodes and connect, you're defining structure. The simulator expands it to primitives before running — this is called elaboration. All runtime behavior ultimately comes from circuits with eval or onTick functions.
You can also write your own behavioral circuits using eval (combinational) or state + onTick (sequential). See the API Reference for details.
Component Resolution
When the system encounters a node:
- Check for
eval/onTickon the circuit — if found, treat as a behavioral leaf - Check for
nodes/connect— if found, elaborate (expand recursively) - Error — circuit has neither
Primitive Reference
Logic Gates
| Primitive | Inputs | Outputs | Description |
|---|---|---|---|
And | a: Bit, b: Bit | out: Bit | AND — true when both inputs are true |
Or | a: Bit, b: Bit | out: Bit | OR — true when at least one input is true |
Not | in: Bit | out: Bit | NOT — inverts the input |
Nand | a: Bit, b: Bit | out: Bit | NAND — false only when both inputs are true |
Nor | a: Bit, b: Bit | out: Bit | NOR — true only when both inputs are false |
Xor | a: Bit, b: Bit | out: Bit | XOR — true when inputs differ |
Xnor | a: Bit, b: Bit | out: Bit | XNOR — true when inputs match |
Buffer | in: Bit | out: Bit | Passes input through unchanged |
I/O
| Primitive | Inputs | Outputs | Parameters | Description |
|---|---|---|---|---|
Switch | — | out: Bit | value (default: 0) | Clickable 1-bit toggle |
Button | — | out: Bit | — | Momentary push button |
Input | — | out: Bus[N] | value (default: 0), width (default: 8) | Editable numeric input |
Led | in: Bit | — | — | Visual LED indicator |
Output | in: Bus[8] | — | — | Multi-bit output sink |
Constant | — | out: Bit | value (default: 0) | Fixed value source |
Probe | in: Bit | out: Bit | — | Debug passthrough |
Arithmetic
All arithmetic primitives support a width parameter (default: 8, options: 4, 8, 16, 32).
| Primitive | Inputs | Outputs | Description |
|---|---|---|---|
Adder | a, b, carry_in | sum, carry_out | N-bit adder with carry |
Subtractor | a, b, borrow_in | difference, borrow_out | N-bit subtractor with borrow |
Multiplier | a, b | product: Bus[2N] | N×N → 2N-bit multiplier |
Comparator | a, b | eq, lt, gt | N-bit comparator |
Incrementer | in | out | Adds 1 (wraps at max) |
LeftShifter | value, shift | result | Logical left shift |
RightShifter | value, shift | result | Logical right shift |
SignedAdder | a, b, carry_in | sum, overflow, carry_out | Signed add with overflow |
SignedComparator | a, b | eq, lt, gt, lte, gte | Signed compare |
SignedMultiplier | a, b | product: Bus[2N] | Signed N×N multiplier |
Bus Operations
All support width parameter (default: 8).
| Primitive | Inputs | Outputs | Description |
|---|---|---|---|
BusAnd | a, b | out | Bitwise AND |
BusOr | a, b | out | Bitwise OR |
BusNot | in | out | Bitwise NOT |
BusXor | a, b | out | Bitwise XOR |
Routing
| Primitive | Inputs | Outputs | Parameters | Description |
|---|---|---|---|---|
Mux | in0, in1, sel: Bit | out | width (default: 1) | 2-to-1 multiplexer |
Decoder | in: Bus[2] | out0–out3: Bit | — | 2-to-4 decoder |
Splitter | in: Bus[8] | out0, out1: Bus[4] | — | Split bus in half |
Splitter8to8 | in: Bus[8] | bit0–bit7: Bit | — | Split bus to individual bits |
Combiner8to8 | bit0–bit7: Bit | out: Bus[8] | — | Combine bits to bus |
BitSlice | in: Bus[8] | out: Bus[N] | low (0), high (7) | Extract bit range [low..high] |
Concat | high, low | out | lowWidth (4) | Concatenate buses |
AddressCombiner | lo: Bus[8], hi: Bus[8] | out: Bus[16] | — | Combine to 16-bit address |
Sequential
These components have clocked state that updates on the rising edge of clk.
| Primitive | Inputs | Outputs | Parameters | Description |
|---|---|---|---|---|
DFlipFlop | d: Bit | q: Bit, q_bar: Bit | — | 1-bit register, captures d on clock edge |
Register | data: Bus[N], we: Bit | q: Bus[N] | width (8), value (0) | N-bit register, writes when we is high |
Key difference: DFlipFlop always captures its input on every clock edge. Register only writes when we (write enable) is high — this lets you hold a value across multiple cycles.
Reset behavior (Verilog export): when the exporter's auto-plumbed rst_n is low, both DFlipFlop and Register snap back to their value arg. In simulation, sim.reset() produces the same effect. Memory primitives (RAM, DualPortRAM) preserve their contents during reset — only writes are suppressed.
Memory
| Primitive | Inputs | Outputs | Parameters | Description |
|---|---|---|---|---|
ROM | addr: Bus[16] | data_out: Bus[8] | baseAddress | Read-only, combinational read |
RAM | addr, data_in, we: Bit | data_out | addressWidth (8), dataWidth (8) | Single-port, combinational read, clocked write |
DualPortRAM | addrA, dataA, weA: Bit, addrB | outA, outB | addressWidth (8), dataWidth (8) | Port A reads/writes, port B read-only |
DualPortROM | addrA: Bus[32], addrB: Bus[32] | dataA: Bus[32], dataB: Bus[32] | — | Two independent read ports, one shared memory. Byte-addressable, returns 32-bit little-endian words. Used for architectures that need simultaneous instruction fetch and data read (e.g. rv32i-board.circuit.ts). |
Memory is initialized by calling the component factory with the memory parameter:
nodes: {
ram: DualPortRAM({ memory: {
0: 42, // address 0 = 42
1: 100, // address 1 = 100
64: 33, // address 64 = 33
} }),
},Reads are combinational (address in → data out immediately). Writes happen on the rising clock edge when we is high.
Display
| Primitive | Inputs | Outputs | Description |
|---|---|---|---|
SevenSegment | in: Bus[4] | — | Hex digit display (0–F) |
HexDisplay | in: Bus[N] | — | Multi-digit hex display |
Screen | dataIn: Bus[8] | addrB: Bus[8] | 8×8 pixel framebuffer display |
RasterDisplay | dataIn: Bus[8] | addrB, scanX, scanY, hblank, vblank | Hardware-accurate raster with scan counters |
The Screen primitive works with DualPortRAM: connect screen.addrB → ram.addrB and ram.outB → screen.dataIn. The screen scans through addresses 0–63 to read pixel data.
I/O Devices
| Primitive | Inputs | Outputs | Description |
|---|---|---|---|
Console | data: Bus[8], we: Bit | — | Memory-mapped text output |
UART_TX | addr, data_in, we, re | data_out | Memory-mapped serial transmit |
NIC_FIFO | addr, data_in, we, re | data_out, tx_data, tx_valid, etc. | Network interface with TX/RX FIFOs |