I have an SSD1306 I2C module written in Verilog, but I suspect the design is the worst possible way to do it
It does work though, I can post the code on Github after Christmas.
It was originally written and debugged using Lattice Diamond for the MachXO2 breakout (7000HE version), but I've synthesized it for iCE40 HX8K as well as Intel/Altera Max10. It
almost fits in an iCE40 HX1K like the iCEstick, but the framebuffer mapping logic is huge and inefficient.
It waits 200ms after reset, sends the display initialization sequence, and then it just refreshes the entire screen over and over from a sequential 8192-bit framebuffer ram made up of 128-bit rows, mapped to the column+page ordering expected by the SSD1306. There are other drawing modes the display can be set to that might make things simpler and more efficient, but I haven't tried them yet.
There's no logic for drawing
to the framebuffer yet. For development I just had it load a manually generated binary file into the framebuffer using
$readmemb() so I could see if the pattern was being drawn correctly or not.