ex_systolic: Elixir as a Deterministic Chip Simulator
A systolic-array library that turns the BEAM into a clocked hardware lab, with strict tick semantics, pluggable space models, and a backend split that keeps simulation honest.
- ex_systolic treats Elixir less like an app runtime and more like a deterministic simulator for clocked hardware.
- Its core trick is a strict tick contract that prevents same-tick feedback and makes timing part of the API.
- The architecture separates space from execution, which keeps topologies swappable without rewriting the simulation engine.
- The project values fidelity and inspectability over throughput, so its real competition is hardware and low-level tooling.
The Clock Is the Product
`ex_systolic` is not trying to be a faster matrix library. It is trying to make a clocked spatial machine legible. That is the twist: Elixir, a language usually praised for distributed systems, is being used here as a way to model hardware timing with discipline.
The repository’s own thesis is simple enough to miss at first glance. The system is only interesting if the timing is correct. Once you accept that, the rest of the design reads like a set of guardrails around determinism.
I will rewrite this later.
Why Elixir Is a Strange But Smart Fit
Elixir is an odd place to build a chip simulator, and that is exactly why it is interesting. The BEAM gives you concurrency primitives, but `ex_systolic` uses them under a hard synchronization rule instead of for free-form messaging.
That makes the runtime feel less like an actor playground and more like a controlled lab. Every process can be parallel, but nothing is allowed to leak across a tick boundary. The result is a simulation model that stays predictable even when the work is distributed.
# Conceptual shape of the PE contract
{new_state, outputs} = step(state, inputs)
# No side effects in the middle of the tick.
# No same-tick reads of freshly written data.
# Determinism comes from the order, not from luck.
Inside One Tick
The heart of the system is a six-step BSP loop: Inject, Read, Execute, Collect, Write, Record. That order is the whole game. It makes the simulation behave like a real clocked device, where new values are not visible until the next cycle.
That sequencing matters more than parallelism. Injecting a value does not make it instantly visible. Reading happens before execution, execution before collection, and writeback only lands for the following tick. The model forbids the kind of immediate feedback that would make timing ambiguous.
Space and Execution Are Split on Purpose
The codebase separates topology from runtime. `ExSystolic.Space` defines how the grid is shaped and connected. `ExSystolic.Array` and the backend handle the act of stepping through time. That split is the reason the project can talk about future topologies without rewriting the simulation engine.
In practice, that means the array is not married to one geometry. A rectangular grid is the default, but the architecture leaves room for toruses, hex grids, or other spatial layouts if the space module changes. The execution contract stays stable.
| Layer | Job | Why it matters |
|---|---|---|
| Space | Defines coordinates and links | Topology can change without changing the clock |
| Array | Holds PE and link state | Keeps simulation state separate from layout |
| Backend | Executes each tick | Different execution strategies can preserve identical semantics |
Processing Elements Stay Pure
The processing element contract is deliberately austere. A PE is a state transformer. It receives a state and inputs, then returns a new state and outputs. Nothing inside that callback should depend on the outside world.
That purity is what makes skewed streams manageable. The `:empty` sentinel exists because the machine has to represent absence as data. In a systolic array, blank slots are not a bug. They are part of the timing choreography.
# Conceptual PE step
{state, inputs} -> {new_state, outputs}
# :empty means "nothing arrived this tick"
# not nil, not failure, just a deliberate timing placeholder
Two Backends, One Contract
The backend split is a practical promise. One path interprets the machine directly. The other partitions work across a pool so larger grids can be simulated in parallel. The important part is that both must agree on the same tick semantics.
| Approach | Strength | Weakness | Best for | How ex_systolic differs |
|---|---|---|---|---|
| Verilog or VHDL | Hardware fidelity | Slower to iterate and harder to inspect | ASIC or FPGA design | `ex_systolic` models the same timing rules in a high-level language |
| C or C++ with GPU libraries | Raw performance | The hardware intent is often hidden behind performance abstractions | Throughput-heavy math | `ex_systolic` prioritizes inspectable timing over speed |
| Typical Elixir concurrency | Elegant process orchestration | Usually not built for synchronous chip-like clocks | Distributed services | `ex_systolic` uses BEAM concurrency inside a strict deterministic boundary |
That is why the partitioned backend is not a different story. It is the same story with more workers. Parallelism is allowed, but only if the tick barrier still enforces the same observable behavior.
Why the Same Grid Can Solve Different Problems
The most interesting part of the design is that the machine is not tied to one algebra. The repo points toward semi-rings, which means the same grid can express different problem classes by changing what counts as add and multiply.
That is the GraphBLAS-style insight. The machine stays the same. The meaning of the computation changes. A systolic array built for GEMM can become a graph algorithm engine if the algebra changes under it.
What This Is and Is Not
`ex_systolic` is not competing with BLAS on throughput, and it is not trying to replace hardware description languages. It is a research-friendly simulator that makes the timing rules explicit, testable, and extensible.
Compared with Verilog or VHDL, it is faster to explore and easier to instrument. Compared with C++ or GPU libraries, it is more faithful to the idea of a clocked array than to raw performance. Compared with ordinary Elixir concurrency, it is far stricter about ordering.
| Approach | Primary goal | Timing model | Best use case |
|---|---|---|---|
| Verilog or VHDL | Hardware implementation | Physical clock semantics | Designing real circuits |
| C or C++ plus GPU libs | Speed | Performance-first | Production math kernels |
| Elixir concurrency | Service orchestration | Asynchronous messaging | Distributed systems |
| ex_systolic | Inspectable simulation | Strict BSP ticks | Research, teaching, and modeling |
The Bottom Line
`ex_systolic` makes a strong, unusual claim: timing can be part of the API. That is what gives the project its character. It uses Elixir not to imitate a web app, but to stage a clocked hardware world with enough discipline to study it.
For anyone who wants to understand systolic arrays without dropping straight into HDL or low-level performance code, that is a real contribution. The library is small, but the idea is clean.





