Systolic Labs open hardware, near Paris

Systolic NPU RISC-V Linux

A systolic INT8 NPU proven on real hardware — and two RISC-V SoCs that boot real operating systems.

The NPU is a parametrised 2-D output-stationary systolic array (N = 2…16) with a verified O(N²) scaling law, a power-on self-test, and a host interface that needs no extra pins. It ran 131/131 INT8 matmuls on a 10,320-LE Cyclone IV across requantisation scales, and a full RV32E SoC drove it on its native bus in 7/7 self-checking vectors. Alongside it: Linux 6.11 on a soft-core CVA6, and unmodified Zephyr SMP on a €30 board.

StatusThe NPU and the CVA6 Linux platform are validated on hardware; the €30 board boots autonomously from its own flash. Current work: merging the two mature platform branches into gen1, then bringing the accelerator into the SoC fabric on the HBM path.

Array
2×2 → 16×16 systolic INT8
Throughput
N² MACs/cycle · verified
On hardware
131/131 matmuls · 7/7 SoC vectors
Toolchain
free · down to sky130

01 NPU

A 2-D systolic array, parametrised in N. INT8 operands flow into N² multiply-accumulate cells; INT32 partial sums are requantised back to INT8 through a pipelined stage, then land in a register file that is memory-mapped to the host. Output-stationary, one weight per cell, and the whole thing scales by changing a parameter.

Two things make it worth taking seriously rather than admiring: the scaling is measured and law-like, and the design self-tests on power-up before it hands the bus to anything else.

Resource scaling — measured, reproduced by one command

NPU resource scaling with array size N
N = 28 wide MACs · 251 cells
N = 432 wide MACs · 720 cells
N = 8128 wide MACs · 2,528 cells
N = 16512 wide MACs · 9,488 cells

The array itself uses exactly N² wide INT8 multipliers — 4, 16, 64, 256 for N = 2, 4, 8, 16. The full design reports 2·N² because the requantise stage adds one scaling multiplier per output. Both the multiplier count and the cell count grow as O(N²). Reproduce any size with syn/yosys/bench_npu.sh 2 4 8 16; functional correctness at scale is checked by make -C sim npu and npu-big against a software reference.

Throughput — a model cross-checked against the RTL

One N×N output tile completes in 3N − 2 cycles in steady state (from the front-end skew schedule), performing up to N² MACs per cycle with every cell active. A 16×16 array therefore does 256 INT8 MACs/cycle at peak. The analytic model is checked against the finite-state machine in mm_frontend.v, not asserted.

Validated on hardware, not in simulation

Hardware validation results for the NPU
NPU, bare131 / 131 INT8 matmuls PASS
Acrossrequantisation scales
Full SoCRV32E core + native NPU bus · 7 / 7 vectors PASS
Read overJTAG — power-on BIST owns the bus first, then releases it
DeviceEP4CE10E22C8 · 10,320 LE · 46 DSP · C8 speed grade
Host linkVirtual-JTAG — reuses the programming cable, no extra pins

The self-checking vectors reproduce to 7/7 PASS, and the ABI between the compiler and the hardware was found wrong only on the board — an rv32i/RV32E mismatch plus a linker RAM-size error that no simulation run had reason to question. The raw Quartus map, fitter, static-timing and flow reports behind this summary are sent on request: these are the traces, not screenshots of them.

Reach — free tools, and a path off the FPGA

Toolchain reach for the NPU
FPGA flowQuartus Prime Lite (free) · Yosys for technology-independent sizing
ASIC flowOpenLane / OpenROAD → SkyWater 130 nm
ASIC synthesis37,002 standard cells at N = 2 — synthesis, not a tape-out
Device ceiling measuredN = 8 is the practical fit on the KU3P; N = 16 overflows its 1,368 DSPs

No paid licence is required at any step, which is the point: an accelerator you can only build with an expensive toolchain is not an open platform. The ASIC figure is a synthesis result and is labelled as one — the tape-out path is documented and versioned, but nothing has been fabricated.

02 SoCs

The substrate the NPU plugs into: two RISC-V platforms, both validated on hardware, at two different scales. One runs Linux out of DDR4; the other runs real SMP firmware on a board that costs less than a meal.

Application-class — Linux 6.11 on a CVA6 SoC running

A complete 64-bit RISC-V SoC — CPU, DDR4 controller, interrupt and timer blocks, a UART and a memory-mapped accelerator slot — boots a real kernel to an interactive shell on a Xilinx Kintex UltraScale+ XCKU5P. Soft-core: there is no hardened CPU in the device.

CVA6 / KU5P platform configuration
FPGAXCKU5P-FFVB676-1-i · Kintex UltraScale+
CPU coreCVA6, RV64IMAC, Sv39 MMU · 1 hart
InterconnectAXI4, with a native ATOP read-modify-write engine in the RAM controller
DDR41 GiB MT40A512M16 · 2400 MT/s · ui_clk 300 MHz (4:1)
Boot chainOpenSBI 1.8.1 → Linux 6.11.11 → BusyBox, static initramfs
Accelerator slotMapped at 0x10000000 and reachable from Linux
ToolsVivado 2025.2

Measured on silicon

Software results measured on the KU5P board
Bootcomplete, to a BusyBox ~ # prompt
standalone: from its own flash, no host in the loop
Flash boot, power-on to shell~85 s against a < 3 s target: ~9 s fabric configuration, ~36 s loader (the software inflate dominates), ~33 s kernel
Usable RAM992 / 1022 MiB · 97 %
sched_clock64-bit @ 50 MHz · 20 ns resolution
Tick timer250 Hz · 4 ms
Kernel image11,097,600 B · 10.6 MiB
Paths meeting timing15/09 build: 99.852 % · 1,228 of 829,505 violated
19/09 build: 100 % · 0 of 206,109 violated
22/09 build: 100 % · 0 of 205,576 violated
Setup WNS15/09 build: −0.221 ns · TNS −81.3 ns
19/09 build: +0.184 ns · TNS 0.000 ns
22/09 build: +0.219 ns · TNS 0.000 ns · all specified constraints met
Hold WHS19/09 build: +0.011 ns · 0 violations of 205,599
22/09 build: +0.012 ns · 0 violations of 205,066
LUT utilisation15/09 build: 161,688 / 216,960 · 74.5 %
19/09 build: 96,231 / 216,960 · 44.35 %
22/09 build: 93,941 / 216,960 · 43.3 %
DSP48E2467 / 1,824 · 25.6 %

Three routed builds are reported separately, and they are not compared. The 19/09 and 22/09 builds report every constraint met and zero failing endpoints, but their endpoint counts are a quarter of the 15/09 build's, so the runs describe different extents of the design. A single figure would hide that, and an earlier figure on this page could not be traced back to a routed report.

"Constraints met" is not a signoff. The flow's acceptance gate is WNS > +5 ns and CLUT < 75 %: occupancy passes comfortably, timing does not. +0.184 ns is an order of magnitude under the gate, as it was before those fixes, so this is a validation, not a signoff. The 19/09 hold margin (+0.011 ns) is inside the re-run noise band — re-runs of identical RTL here move hold by tens of picoseconds — so it is not margin.

And the unclosed run is stated on purpose. The 15/09 build's WNS is −0.221 ns, with 1,228 endpoints violated. A page that only publishes its good numbers is not evidence.

The traces. The flashed bitstream is ku5p_soc_top.bit, built 19/09 on an xcku5p-ffvb676 with Vivado 2025.2: sha256 ed122006c80b760de50b06f6803e4cc3b1e3d0316684d361bfd9a60b5cfe5e63, HEAD e959e04. The routed timing and utilisation reports behind every figure above are sent on request: these are the traces, not screenshots of them.

A second KU5P design meets timing. The PCIe standalone top routes with 0 of 24,943 endpoints violated, WNS +0.994 ns, 23/09. It is a different design from the SoC above, and it is reported as such.

What it demonstrates

  • A Linux-capable 64-bit RISC-V SoC fits in a mid-range UltraScale+ with no hardened processor.
  • Full boot out of DDR4 with the kernel at 0x100200000, MMU Sv39 and write-through caches.
  • A working atomic path to DDR4 through an ATOP→RMW engine. The kernel's scheduler depends on it.
  • The accelerator slot is mapped and reachable from Linux in the same design — the integration point for the NPU above.

Microcontroller-class — Zephyr SMP on a €30 module running

A complete SoC on a Colorlight i9+ v6.1 (Artix-7 XC7A50T, 8 MB SDRAM, 25 MHz): two Ibex RV32IMA cores, a 64-bit AXI4 crossbar, an SDRAM controller, a CLINT, a 16550 console, a RISC-V-spec interrupt controller, a cross-core LR/SC exclusivity monitor, and free slots for an accelerator and a DMA. Above it runs Zephyr RTOS v4.1.0 in SMP — unmodified kernel. The board boots from its own SD card with no host attached.

Measured on the board

Results measured on the Colorlight i9+ board
Cross-core atomics — LR/SC20,000 / 20,000 exact
Cross-core atomics — AMO20,000 / 20,000 exact
Interrupt-driven console400 / 400-byte burst, 0 FIFO overruns
Throughput — interrupt11,697 B/s
Throughput — polled136 B/s · ×86 slower
SMP stress, 6 threadsPASS · no lost IPIs
Timing — WNS+9.691 ns · gate +5 ns
Timing — WHS+0.042 ns
LUT utilisation56.6 % · gate 75 %
Commits on the branch272 · 494 files · +67,470 / −641

Autonomous boot — the full chain

SD, filesystem and standalone boot results
Card identifiedSDHC · OCR C0FF8000 CCS=1 · MBR 55 AA
SD stackdisk init ret=0 · 7,864,320 sectors · 3.75 GiB
FAT32mounted · write + read-back 1,000/1,000 B · checksum 2726712C
FSBL, bare-metal2,874 B · SD init with no OS · PASS
Boot ROM4,130 B · 50 % of 8 KiB BRAM · all gates pass
Autonomous bootflash written · real power-cycle · FPGA configures from its own flash
Configuration flash16 MiB Macronix MX25L12833 · backed up before writing
Validation before jumpmagic + length + checksum
Dual-hart contracthart 1 released via MSIP[1], after validation
Gates validated7 · none skipped

No FATFS in the FSBL and no OS: the payload is read at a fixed LBA by design, and the checksum is mandatory before the jump. The configuration flash stays rewritable over JTAG even after a failed boot, so the board cannot be lost — which is what turns flashing it into a routine step instead of a risk.

What actually broke

  • A wrong boolean in the atomic unit. The ATOP clear operation computed a&b where the architecture requires a&~b. Linux's clear_tsk_need_resched() uses that operation; it silently did nothing, the reschedule flag stayed set, and schedule() livelocked. One operator, three layers up, a hung kernel — and nothing in simulation had any reason to question it.
  • Inverted chip-select polarity in the SD frame — every command left the card deselected, so no response was ever possible.
  • MOSI and MISO crossed at the micro-SD module — invisible to a continuity test, because a beep proves there is no break, not that two wires are not swapped.
  • A missing release callback in the SPI driver, called on the nominal path — it jumped to address 0. A hardware crash wearing the costume of a filesystem bug.

Why the console is polled here. The RISC-V interrupt controller on this platform does not implement the SiFive register layout, so its device-tree node was removed rather than papered over — the kernel would have faulted on the first interrupt. It is a diagnosed incompatibility with a named cause, deferred deliberately, not an unfinished feature.

03 Platform

The platform targets the buyers that closed silicon excludes or overcharges: European research groups, integrators under GDPR and AI Act pressure, and anyone who needs a datapath they can actually read.

Two things that rarely meet are combined here. A reconfigurable systolic accelerator, proven on hardware and reproducible from a free toolchain. And a software-recompilable RISC-V core with an open, auditable instruction path — already proven up to a Linux userspace. Underneath both sits HBM-class bandwidth that most competing small accelerators simply do not have.

The design posture is deliberate about what this is not:

  • Not a GPU competitor. On raw TOPS/$ and TOPS/W, dedicated edge-AI parts win, and any claim otherwise is marketing.
  • Not a commodity edge-AI part. Camera-tile inference in volume belongs to Hailo and Axelera.
  • Not a cloud service. It is a card and a toolchain, on your bench or in your rack.

What it wins on is the combination those alternatives cannot offer: an auditable open RTL path, a reconfigurable datapath, HBM-class bandwidth in a 75 W half-height card, and a provenance that stands up to a procurement filter written as "no third-country-controlled supplier".

04 Hardware

Proven — OMDAZZ / EP4CE10 NPU validated

EP4CE10 platform specifications
DeviceEP4CE10E22C8 · Cyclone IV E · 10,320 LE
Hard blocks46 DSP · 414 Kb M9K
ToolchainQuartus Prime 25.1 Lite — free edition
Host linkVirtual-JTAG over the USB-Blaster programming cable
In the fabricNPU + register file + power-on BIST + reset synchroniser

Proven — Kintex UltraScale+ XCKU5P running Linux

KU5P platform specifications
FPGAXCKU5P-FFVB676-1-i · Kintex UltraScale+
CPUCVA6 RV64IMAC · Sv39 · 1 hart · soft-core
Memory1 GiB DDR4 (MT40A512M16) · 2400 MT/s
ClocksSoC 50 MHz · ui_clk 300 MHz
RuntimeOpenSBI 1.8.1 → Linux 6.11.11 → BusyBox

Proven — Colorlight i9+ v6.1 running Zephyr

Colorlight i9+ platform specifications
DeviceArtix-7 XC7A50T
Memory8 MB SDRAM
Clock25 MHz
Cores2× Ibex RV32IMA · 64-bit AXI4 crossbar
RuntimeZephyr RTOS v4.1.0, SMP, unmodified kernel
Cost€30–50 module + JTAG dongle

In progress — AMD Alveo U50 bring-up

The scale-up target, chosen on price per gigabyte-per-second rather than peak spec: an HBM2 card is what lets a 16×16 array run without starving. Now on the bench, with the shell and the PCIe path under bring-up. No result is published here until it has been measured on the card.

Alveo U50 specifications
CardAMD Alveo U50 (A-U50-P00G-PQ-G)
Memory8 GB HBM2 · 316 GB/s peak
Logic872 K LUTs · 5,952 DSP slices
On-chip51.8 Mb BRAM + 180 Mb UltraRAM
FormatHalf-height, half-length, single slot, passive · 75 W

Portability. AMD has issued the end-of-life notice for the U50 (XCN26004): last-time-buy 30 October 2026, last-time-ship 2 April 2027. That closes the new-and-warranted channel only — the used market is unaffected. The design is kept portable at RTL level to the U55C (16 GB, 460 GB/s) and V80 (32 GB, 820 GB/s).

05 Log

Working notes, dated when the work lands — or labelled by the gate it closes. Threads still running are marked open.

  1. The NPU runs on hardware

    131/131 INT8 matmuls pass on an EP4CE10 across requantisation scales, read back over JTAG. The same day, a full RV32E SoC boots from on-chip memory, drives the NPU on its native bus, and self-checks 7/7 vectors — an ABI mismatch and a linker RAM-size error surfaced only on the board.

  2. BlackParrot fits a KU3P at 100 MHz

    A complete BlackParrot SoC closed timing on an XCKU3P. Kept as a reference point for what a manycore-oriented core costs on mid-range fabric.

  3. XiangShan: the estimate said 74 %, the measurement said 240 %

    An out-of-context synthesis of XiangShan's MinimalConfig reported 74 % of an XCKU3P's LUT6s. The real figure — a post-optimisation hierarchical report, on a larger device — was 520,681 LUTs against 216,960 available: 240 %. Stacking every optimistic lever (memories into BRAM, issue queues halved, predictors dropped, FPU and L3 removed) still lands near 291 K, above the 217 K budget, and the irreducible out-of-order core alone is ~350 K. MinimalConfig does not fit at any practical configuration. The port was tried on four platforms before the call was made to stop — and the estimate was wrong by roughly 3×, which only building it revealed.

  4. CVA6 takes the KU3P, and stalls in the kernel

    Boot hang localised to vfs_caches_init / mnt_init — the point where the kernel first needs working memory mapping. Useful precisely because it was wrong: it moved the target to a board with real RAM.

  5. Market and positioning study

    Four verticals scored on sales friction and willingness to pay. Conclusion: lead with academic and R&D prototyping, keep edge AI and defence behind it, do not attack the data centre.

  6. HBM FPGA sourcing audit

    Every FPGA with on-package HBM2/HBM2e compared on bandwidth, LUT count, price and lifecycle status. Alveo U50 selected as the scale-up reference.

  7. The CVA6 kernel still arrives over UART

    11 MB takes about 17 minutes at 115,200 baud — tolerable for bring-up, unusable for iteration. The same SD-boot approach now proven on the Ibex platform is the fix.

  8. Standalone boot — FSBL in the Boot ROM

    A 4,130-byte bare-metal FSBL lives in BRAM and boots the payload itself: BRAM → SD card → validate → jump. The host programmed the FPGA and streamed nothing.

  9. It boots on its own now

    The 16 MiB configuration flash was backed up first — full image, hash taken — then written, then verified by a real power-cycle with nothing else attached. That closes the chain end to end: RTL → SD card → filesystem → second stage → Boot ROM → flash. Seven gates, none skipped.

  10. Merge to gen1, then the accelerator into the fabric

    The two mature platform branches integrate into a single trunk, and from there the NPU moves from a proven standalone design into the memory-mapped slot already waiting for it.

06 Work with me

Accelerator engineering

Systolic and custom accelerator design: array sizing, quantisation and requantise datapaths, host interfaces, and self-test. Delivered with the scaling numbers, not a promise about them.

SoC and bring-up

RISC-V integration, AXI interconnect, DDR and memory-controller bring-up, Linux and BSP work, FPGA flows down to a bootable board. Including the bring-up nobody else wants to do.

Research access

For labs and academic groups: design support, a reproducible build-and-signoff flow, and measured results you can cite — from synthesis to silicon.

Email [email protected]

I am one engineer. If the scope is real, I will tell you what I can deliver and what I cannot — that conversation takes less time than a proposal.