SH1107 OLED (I²C) driver, plus virtual hardware testbench.
This repository has been archived on 2024-03-22. You can view files and clone it, but cannot push or open issues or pull requests.
Go to file
Amelia Cuss 8f0423f1c8 redo on stripped hdx:
nix develop .?submodules=1 then python -m sh1107 vsh works, but nix
build doesn't.  I think I don't care right now.
2024-02-17 19:33:10 +11:00
doc README: update 2023-05-23 10:39:40 +10:00
sh1107 redo on stripped hdx: 2024-02-17 19:33:10 +11:00
vsh everything: up-to-date. 2024-01-18 16:44:26 +11:00
.gitignore ci: WIP for using nix build/github actions. 2023-08-02 18:18:55 +10:00
.gitmodules everything: up-to-date. 2024-01-18 16:44:26 +11:00 README: link to canonical source. 2024-01-17 22:27:51 +11:00
flake.lock redo on stripped hdx: 2024-02-17 19:33:10 +11:00
flake.nix redo on stripped hdx: 2024-02-17 19:33:10 +11:00
pdm.lock ci: WIP for using nix build/github actions. 2023-08-02 18:18:55 +10:00
pyproject.toml amaranth: complete changes. 2023-08-21 14:28:52 +10:00
woof amaranth: complete changes. 2023-08-21 14:28:52 +10:00


Build status

This repository is a testbed for exploring Amaranth while learning digital design. It consists of a basic driver for SH1107-type OLEDs over I²C such as the Pimoroni 1.12" 128x128 monochrome OLED, a read/write I²C controller, plus a simple SPI flash reader. The driver supports commands akin to old BASIC: CLS, PRINT, LOCATE. The classic IBM 8x8 font is used to this end.

Execute the package to see what it can do:

$ py -m sh1107 -h
usage: sh1107 [-h] {test,formal,build,rom,vsh} ...

positional arguments:
    test                run the unit tests and sim tests
    formal              formally verify the design
    build               build the design, and optionally program it
    rom                 build the ROM image, and optionally program it
    vsh                 run the Virtual SH1107

  -h, --help            show this help message and exit

The current test deployment targets are:

  • iCEBreaker (Crowd Supply, 1BitSquared).
    • Connect PMOD1 A1 to SDA, A2 to SCL.
  • OrangeCrab (1BitSquared).
    • Connect the pins named SDA and SCL.
    • The code currently expects you have rev 0.2 with an 85F like I do. It's trivial to add support for rev 0.1 and/or the 25F.


If no version is specified, the most recent release or the version in your package manager is probably fine, and if neither concept applies, just try the latest commit.

For a detailed guide on installing specific versions of some of these tools, please see Installing an HDL toolchain from source. On Nix, hdx packages everything — nix develop '.?submodules=1'/nix-shell will use it.

To run at all:

To run vsh:

To build and deploy:

To run formal tests:


  • try QSPI.
  • OrangeCrab: try on-board DDR3 instead of EBR.


Maybe the most interesting thing right now is the Virtual SH1107 for testing the gateware. It emulates the internal state of the SH1107 device — what you see rendered is what you should see on the display.

screenshot of the Virtual SH1107 testbench photo of the OLED device being run on an

Initially this was implemented in Python and ran cooperatively with Amaranth's own simulator, like the unit tests, but it was pretty slow. It's now written in Zig, and interacts with the simulated hardware running on its own thread by compiling it to C++ through Yosys's CXXRTL backend.

$ py -m sh1107 vsh -h
usage: sh1107 vsh [-h] [-i] [-f] [-c] [-s {100000,400000,2000000}] [-t TOP]
                  [-v] [-O {none,rtl,zig,both}]

  -h, --help            show this help message and exit
  -i, --whitebox-i2c    simulate the full I2C protocol; by default it is
                        replaced with a blackbox for speed
  -f, --whitebox-spifr  simulate the full SPI protocol for the flash reader;
                        by default it is replaced with a blackbox for speed
  -c, --compile         compile only; don't run
  -s {100000,400000,2000000}, --speed {100000,400000,2000000}
                        I2C bus speed to build at
  -t TOP, --top TOP     which top-level module to simulate (default:
  -v, --vcd             output a VCD file
  -O {none,rtl,zig,both}, --optimize {none,rtl,zig,both}
                        build RTL or Zig with optimizations (default: both)


By default, the I²C circuit is stubbed out with a blackbox that acts close enough to the real controller for the rest of the design, and the Virtual SH1107 spies on the inputs to the blackbox directly. This is fast.

At the most fine-grained level (vsh -i), it responds to the gateware by doing edge detection at I²C level, spying on the I²C lines. This method is faster than the pure Python version I started with, but still slow enough to take several seconds to clear the screen when not compiled with optimizations.

SPI flash

Sequences of SH1107 commands used by the driver are packed into a ROM image which is separately programmed onto the on-board flash. The driver reads the contents into RAM on startup over SPI.

By default, the SPI flash reader component is stubbed out with a blackbox, which emulates the component's interface, returning data bytes directly to the OLED driver from the ROM embedded in the build.

This blackbox can be replaced with a whitebox (vsh -f), which emulates at one level lower, emulating the SPI interface itself, returning data bitwise to the flash reader.