LED Matrix Driver Get in touch

Prototype · Hardware and firmware

LED Matrix Driver

A 128×64 RGB panel driven from an iCE40, with the timing worked out in SystemVerilog.

BCM · one row period on a 16×4 patch of the panel

8 bit planes, MSB first510 of 510 µs

b7b6b5b4

256 µs1286432 … 2

After gamma 2.4
96
Bit planes
01100000
Lit per row
192 µs
Row period
513.6 µs
A simulation in your browser of the outlined pixel and its neighbors, using the design’s timing: 2.0 µs per LSB, 16 rows per refresh, and the 121.7 Hz the testbench measured. The design passes simulation and synthesis but hasn’t driven a real panel yet.
Title
LED Matrix Driver
Dwg no.
Q6-001
Rev
2026.1
Status
Prototype
Drawn by
DWMM Holdings

Notes

What it does.

A SystemVerilog driver for a custom 128×64 RGB LED matrix on a Lattice iCE40UP5K. An STM32 sends frames over SPI. The FPGA double-buffers them, applies gamma correction, and drives the panel with 8-bit binary code modulation at 120 Hz or more, with global dimming.

  1. 01

    8-bit binary code modulation at 120 Hz or higher, with global dimming

  2. 02

    Double-buffered frames streamed over SPI from an STM32

  3. 03

    cocotb testbenches checked against a Python model of the panel

  4. 04

    An all open-source toolchain: yosys, nextpnr-ice40, icestorm and Verilator

Where it stands

Six gates passed. Hardware is next.

The design was built against six acceptance gates, each checked by a cocotb test or a tool report. All nine tests pass. None of this has run on a real panel yet: the board, with an iCE40UP5K, an STM32L432KC and USB-C, is drawn as a KiCad schematic and its layout hasn’t started.

  1. G0

    Toolchain

    OSS CAD Suite and cocotb run the Verilator lint and the simulation.

  2. G1

    SPI ingest

    ID reads back 0xA5. Frames land gamma-corrected at the right SPRAM addresses, BRIGHT and SWAP latch, and a CS abort mid-byte recovers.

  3. G2

    Scan skeleton

    16 addresses, 8 latches per row, 128 clocks per shift. Latch and address only change while the panel is blanked.

  4. G3

    Image

    A Python model of the panel rebuilds a random frame from the waveforms. Every LED’s on-time matches gamma(pixel) × T_LSB exactly.

  5. G4

    Refresh and dimming

    121.7 Hz, measured by the testbench. Brightness 128 halves every LED’s on-time, and 0 blanks the panel.

  6. G5

    Timing closure

    nextpnr reports 39.23 MHz against a 33 MHz gate, using 16% of the iCE40UP5K. make bin builds the bitstream.

SPI protocol

Five commands from the STM32.

SPI mode 0, MSB first, up to 30 MHz. The first byte after chip select is the command. A full frame takes about 6.6 ms at 30 MHz.

CommandWhat it does
0x01 FRAME24,576 bytes: rows 0 to 63, columns 0 to 127, RGB. Written through the gamma table into the back buffer.
0x02 SWAPSwap front and back buffers at the next frame boundary, so there’s no tearing.
0x03 BRIGHTOne byte of global brightness. The refresh rate stays the same; the panel just stays dark longer.
0x0E STATUSReturns 0x50 plus a swap-pending bit, so the STM32 knows when it’s safe to write.
0x0F IDReturns 0xA5. A quick check that the link works.

Specification

What it’s built with, and when.

Pulled from the repository each time this site is built.

State
Prototype
Discipline
Hardware systems
Shape
Hardware and firmware
Started
Jul 2026
Last change
Jul 2026
Commits
1
  • SystemVerilog
  • iCE40
  • cocotb
  • Python
  • yosys
  • SystemVerilog 70%
  • Python 27%
  • Makefile 3%

Next

Build on LED Matrix Driver

Ask about licensing the platform, bringing up a board, or contract firmware work.

This site isn't finished. Anything on it may still change, and none of it is binding until the site is live.