← portfolio  /  MMACU
Project note

This version is no longer actively maintained. Known bugs and identified issues may remain unresolved here.

The actively maintained design is lives under DMCTP , which builds on this initial architecture with expanded formal and functional verification, improved performance, and improved static timing analysis results.

SYSTOLIC ARRAY GEMM ACCELERATOR · ZYNQ

A systolic array
that does matrix multiply
in hardware.

Output stationary systolic array on an Zynq SoC, with the INT8 and FP8 data paths.

Precision
INT8 / FP8
Target
Zynq SoC
Verify
Verilator
FIG. 0 - Systolic array dataflowCYCLE 000
operand inflight partial sum drained idle PE (PE-Processing Elements)
System architecture

How does the Accelerator work end to end?

FIG. 1
OFFLINE compiler.py matmul → instr. stream .bin ZYNQ · PROCESSING SYSTEM (ARM) user_test matmul_test.c runtime.c buffer / job mgmt driver.c (MMIO) AXI INTERCONNECT ZYNQ · PROGRAMMABLE LOGIC (FPGA) MMACU — systolic array core frontend ctrl + hazard regfile

The host (ARM PS) side builds the program (text + data) and puts it into the unified memory (DRAM). Then the PS signals the accelerator over MMIO (AXI GP). On the PL side, the frontend fetches the commands/instructions, and the LSU (load-store unit) loads the data. The register file supports data reuse and buffering. Then the systolic array itself performs the actual MACs (multiply accumulate).

Implementation results

What came out of synthesis and simulations

TABLE 1
Vivado timing utilization and device summary
Vivado
Timing closed at 100 MHz; utilization report and device view of 16x16 INT8 array implementation.
Simulation results for FP8 and INT8
cocotb
Both INT8 and FP8 runs matched the NumPy golden model.
Design structure tree and block design
Structure
Project source tree, and the block design.
Board and terminal screenshot
Hardware
Hardware bring up and the run log from the Zynq target.
Documentation

Design notes

DOC

Overview

A configurable output stationary systolic array GEMM accelerator (D = AᵀB + C), implemented in RTL. The design is optimized for fully pipelined execution with high throughput and supports both integer and floating point FMA operations. The main goal is continuous GEMM streaming with minimal stalls, avoiding diagonal scheduling by using row/column major input streaming.

Architecture and dataflow

The accelerator is built around an output stationary systolic array, where partial sums remain in the processing elements while A and B are streamed through the array. Data is fed in row major streaming format. A small control block orchestrates load, compute, and drain phases, while a register file/SRAM bank enables operand reuse across cycles instead of repeatedly fetching from DRAM.

Memory and streaming model

The design supports flexible A, B, and C streaming modes, including preload and fully streamed C. Ping pong buffering is used in MAC PEs to overlap computation with memory transfers, ensuring the systolic array remains busy while the next tile is being prepared. This enables back to back GEMM execution with zero inter kernel stalls.

Performance

The array achieves nxn outputs every n cycles after pipeline fill. For instance for 4x4 matrices, the pipeline fills in 10 cycles and then produces one result once every 4 cycles. The design is fully pipelined and optimized for sustained throughput rather than single operation latency.

Verification

The design was verified using a Verilator based testbench against a NumPy golden reference model. Both integer and floating point modes were tested, with directed cases ensuring correctness across different matrix sizes and streaming configurations.

Hardware implementation

The accelerator was implemented on a Zynq-7020 FPGA to validate functionality in hardware. The Processing System communicates with the accelerator via AXI GP, while AXI HP is used for DRAM access. The design closes timing at 100 MHz and includes dedicated control logic for orchestration, along with buffering structures for data reuse and pipeline stability.