P44 is the showpiece. A FreeRTOS task on the chip computes 96×96
RGB565 plasma frames into a memory-mapped framebuffer; the testbench
dumps each frame to disk on an MMIO_FRAME_READY pulse; a Python +
pygame viewer plays them back as a real window.
Status: RTL pass. 4 frames dumped at 7.5M clocks. Verified visually - both end frames are real plasma patterns with evolving structure. The chip is rendering animation.
How the bridge works
The chip doesn’t know there’s a “screen.” It writes pixels to a
contiguous memory region (0x00030000 for 96×96 RGB565 = 18432
bytes), then writes the frame number to a new MMIO address
(0x10001ffc). One thing in RTL, two things in software, one
thing in the testbench:
| layer | what’s new |
|---|---|
| RTL | MMIO_FRAME_READY register at 0x10001ffc - same shape as P43’s MMIO halt port. Pulses frame_strobe for one cycle on write. |
| Testbench | On frame_strobe, peek the framebuffer region of memory and write frames/frame_NNNN.bin. |
| Software | Render task computes plasma into *(uint16_t *)0x00030000[], writes MMIO_FRAME_READY after each frame. |
| PC viewer | app/viewer.py opens a pygame window, decodes the dumped RGB565 frames, plays back. |
That’s the entire framework. No SPI peripheral. No display controller. No new core logic.
What you get
Run:
make -C projects/44_framebuffer_demo/test \
FREERTOS_KERNEL=/tmp/FreeRTOS-Kernel run
make -C projects/44_framebuffer_demo/test viewer
The pygame window shows four frames of plasma evolution rendered
entirely by the chip’s CPU. Each frame is a deterministic
function of (x, y, t) evaluated 9216 times per frame at ~1.88M
cycles per frame on the multi-cycle FSM core.
Why this is the cool demo, not the LCD demo
The earlier roadmap mentioned an SPI display demo. The user’s question was “if we did it on a real LCD wouldn’t it be the same speed as on a PC screen?” Answer: yes. The bottleneck is the chip’s pixel-generation rate (~13 FPS at 96×96 with our compute budget), not the transport. So:
- Sim → file → PC window works today, no hardware to buy.
- Chip → SPI → real LCD is the same software with one line changed. Lands when we add a real SPI peripheral.
- Chip → SPI → USB-SPI bridge → PC window is the chip-on-real- hardware version that also works. Same chip output, different sink.
P44 is the cheapest first version. The SPI variants are future ladder rungs.
Limits
- 96×96, not 240×240. The real ST7789 we’d eventually drive is 240×240. Our FSM core renders the smaller frame in ~75 ms; scaling to 240×240 would put us around ~2 FPS and slow the simulator down meaningfully. For a first demo we kept it small.
- 4 frames, not endless. Testbench would happily dump thousands but iverilog gets slow. Four is enough to see the animation evolve.
- Plasma, not Mandelbrot. Triangle-wave math is cheap and runs at compute-bound full speed. Mandelbrot at depth 64 would take multiple seconds per pixel on this core; we’d want pipelining first.
Files
src/top.sv- RTL withMMIO_FRAME_READYadded (everything else verbatim from P43).app/main.c- FreeRTOS render task with the plasma formula.app/viewer.py- pygame window, RGB565 decode, scale, loop.test/tb_framebuffer.sv- dumps the framebuffer on every strobe.test/Makefile- newmake viewertarget.
What does not change
- CPU core, ISA, trap path, halt port, MMIO router, FreeRTOS port - all verbatim from P43.
mimpidbumps to0x44.
Harden status
NOT RUN. The expected delta is ~35 cells over P43 (frame_seq
register + frame_strobe flop + address decode). Hardenable
trivially if we want a real fab-shaped artifact, but not the point
of P44.
What just happened?
P43 closed the FreeRTOS arc with a printout. P44 gives it a face. We can point at a window on the screen and say “this is the chip running” - the same chip we hardened to a sky130A GDS, the same FreeRTOS we boot, the same MMIO halt port that says “done.”
The chip is now an animation generator that runs an RTOS.