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
256 µs1286432 … 2
- After gamma 2.4
- 96
- Bit planes
- 01100000
- Lit per row
- 192 µs
- Row period
- 513.6 µs
- 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.
- 01
8-bit binary code modulation at 120 Hz or higher, with global dimming
- 02
Double-buffered frames streamed over SPI from an STM32
- 03
cocotb testbenches checked against a Python model of the panel
- 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.
- G0
Toolchain
OSS CAD Suite and cocotb run the Verilator lint and the simulation.
- 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. - G2
Scan skeleton
16 addresses, 8 latches per row, 128 clocks per shift. Latch and address only change while the panel is blanked.
- 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.
- G4
Refresh and dimming
121.7 Hz, measured by the testbench. Brightness 128 halves every LED’s on-time, and 0 blanks the panel.
- G5
Timing closure
nextpnr reports 39.23 MHz against a 33 MHz gate, using 16% of the iCE40UP5K.
make binbuilds 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.
| Command | What it does |
|---|---|
| 0x01 FRAME | 24,576 bytes: rows 0 to 63, columns 0 to 127, RGB. Written through the gamma table into the back buffer. |
| 0x02 SWAP | Swap front and back buffers at the next frame boundary, so there’s no tearing. |
| 0x03 BRIGHT | One byte of global brightness. The refresh rate stays the same; the panel just stays dark longer. |
| 0x0E STATUS | Returns 0x50 plus a swap-pending bit, so the STM32 knows when it’s safe to write. |
| 0x0F ID | Returns 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%
Related work
Built alongside LED Matrix Driver
Next
Build on LED Matrix Driver
Ask about licensing the platform, bringing up a board, or contract firmware work.