Sega Master System / SG-1000 for ULX3S (lawrie__ulx3s_sms)
| Field | Value |
|---|---|
| Upstream | https://github.com/lawrie/ulx3s_sms |
| Reviewed at | 13c2361a50 (upstream date 2023-05-19), reviewed 2026-09-28 |
| License | No repo-level LICENSE file (verified with find). Per-file: mostly no header (own code, src/sms.v, src/video.v, sn76489.v); imported cores carry their own terms — TV80 Z80 core is MIT (src/Z80/tv80_core.v), SDRAM controller is GPL-3.0-or-later (Till Harbaum/MiST), OSD/SPI helper cores are BSD (EMARD). The included Sega BIOS ROM is proprietary Sega firmware, not open-source — see Open questions |
| HDL / framework | Verilog only (.claude/memory/catalogue.tsv) |
| Toolchain | open: yosys -p "synth_ecp5 -json ..." (ULX3S) / yosys -p "read -sv ...; hierarchy -top sms; synth_ecp5 -abc9 -json ..." (ULX4M) → nextpnr-ecp5 --<device> --package CABGA381 --timing-allow-fail --freq 25 → ecppack --compress |
| Programmer | ujprog (ULX3S, ulx3s/ulx3s.mk prog target); fujprog / dfu-util -a 0 -D ... -R (ULX4M, ulx4m/ulx4m.mk) |
| Target FPGA(s) | 85F default (README: “It currently defaults to an 85F board. To use a 12F add DEVICE = 12k to the Makefile”); ulx3s/ulx3s.mk sets DEVICE ?= 85k; ULX4M build (ulx4m/ulx4m.mk) targets um-45k |
| Board revision(s) | ulx3s/ulx3s_v20.lpf (ULX3S), ulx4m/ulx4m_v002.lpf (ULX4M) |
| Activity | Single commit reviewed: 13c2361a50 dated 2023-05-19 (git -C original_sources/lawrie__ulx3s_sms log -1 --format=%cs). .claude/memory/history.tsv: first commit 2020-12-25, 46 commits total (clone is shallow, no further history) |
What the gateware does
A Sega Master System (and SG-1000-compatible) games console, entirely in Verilog, with HDMI (+ optional VGA) output at 640×480@60Hz and joypad-1 input mapped to the board buttons:
src/sms.v(top for ULX3S build) /src/ulx4m_sms.v(ULX4M variant, addssrc/flash_loader.v+src/flashmem.vfor flash-resident ROM loading) — top-level modulesms, wires together the CPU, VDP, SDRAM cartridge store, audio and OSD.- CPU:
src/Z80/tv80_*.v— the TV80 Z80-compatible core (tv80_core.v,tv80n.v,tv80_mcode.v,tv80_reg.v,tv80_alu.v), MIT-licensed (Guy Hutchison, opencores, based on Daniel Wallner’s VHDL T80). - Video/VDP:
src/video.v— a from-scratch TMS9918-compatible VDP implementation (legacy modes for SG-1000 compatibility; does not reproduce original VDP timing, per README), feedingsrc/hdmi.v(topHDMI_out) for GPDI/TMDS output. - Audio:
src/sn76489.v(SN76489 PSG, no license header found),src/tone_generator.v,src/sigmadelta.v(sigma-delta DAC foraudio_l/audio_routputs). - Cartridge storage:
src/sdram.v— MiST-board SDRAM controller (Till Harbaum, GPL-3.0-or-later, same file family as inlawrie__ulx3s_examples); ROM cartridge is loaded into SDRAM and executed from there using the Sega memory mapper (README: “the few games that do not use the Sega mapper will not work”).src/ram.v/src/vram.v/src/rom.vprovide BRAM for 8 KB console RAM, 16 KB VRAM, and the 32 KB BIOS ROM. - On-screen display / game loading —
src/osd/(the block this page focuses on, reused by many retro ports): an ESP32-driven SPI OSD stack overlaid on the HDMI output, triggered by pressing all four direction buttons; browses SD-card/flash.smsfiles and streams the selected ROM into SDRAM. MicroPython side lives inesp32/osd/osd.py(generic multi-platform file browser/loader shared across ports — it dispatches to per-console loaders such asld_nes.py,ld_ti99_4a.py,ld_msx.py,ld_zxspectrum.py,ld_orao.py,ld_vic20.py,ld_trs80.py; onlyld_nes.pyships in this repo’sesp32/osd/— no.sms-specific loader is present,.smsfiles are matched directly by extension atesp32/osd/osd.py:183and streamed generically). - Displays:
src/spi_display/lcd_video.v(toplcd_video, ST7789 SPI display core, same file as inemard__ulx3s-misc/lawrie__ulx3s_examples) +hex_decoder_v.v, used for optional LCD/hex diagnostics (c_lcd_hexparameter). - Clocking:
src/lattice/ecp5pll.sv(topecp5pll), identical BSD-licensed EMARD parametric PLL seen throughout the other two repos in this review batch.
Known issues (from README.md): audio needs improving, joypad 1 has problems, a vertical colour bar appears on the left in some games, VDP edge cases are wrong, and roughly 15 named games hang, crash, or show screen corruption (Asterix, Baku Baku Animal, Chop Lifter, Dracula, Fantastic Dizzy, Jungle Book, Lemmings, Lion King, Miracle World, Ms Pacman, Outrun, Space Harrier, Spell Caster, Wanted, Zaxxon 3D).
Structure
src/ core RTL (sms.v top, video.v VDP, sdram.v, sn76489.v, hdmi.v, ...)
src/Z80/ TV80 Z80-compatible CPU core (MIT)
src/osd/ ESP32 SPI OSD stack: osd.v, spi_osd.v, spirw_slave_v.v, spi_ram_btn.v (+ font/mem files)
src/spi_display/ SPI ST7789 display core (shared with ulx3s-misc/ulx3s_examples)
src/lattice/ ecp5pll.sv (shared EMARD PLL)
ulx3s/ ULX3S build: Makefile, ulx3s.mk, ulx3s_v20.lpf
ulx4m/ ULX4M build: Makefile, ulx4m.mk, ulx4m_v002.lpf
esp32/osd/ MicroPython OSD client (osd.py, ld_nes.py)
roms/ bios.mem (readmem-format BIOS init), bios13fx.sms (Sega Europe/USA v1.3 BIOS binary,
proprietary), hello_world.asm/.sms (test cartridge), tomem.py (bin→mem converter)
How to build
Not run (read-only review). From README + ulx3s/Makefile/ulx3s.mk:
cd ulx3s
make prog # DEVICE defaults to 85k; add "DEVICE = 12k" to ulx3s/Makefile for a 12F board
Internally: yosys -p "synth_ecp5 -json toplevel.json" $(VERILOG) → nextpnr-ecp5 --85k --package CABGA381 --timing-allow-fail --freq 25 --textcfg ... --lpf ulx3s_v20.lpf → ecppack --compress → ujprog. VERILOG in ulx3s/Makefile lists sms.v, ram.v, vram.v, rom.v, the five Z80/tv80_*.v files, video.v, hdmi.v, sn76489.v, tone_generator.v, sigmadelta.v, lattice/ecp5pll.sv, sdram.v, spi_display/{lcd_video.v,hex_decoder_v.v}, and osd/{osd.v,spi_osd.v,spi_ram_btn.v,spirw_slave_v.v}. ULX4M build (ulx4m/Makefile + ulx4m.mk) adds flash_loader.v/flashmem.v, uses ulx4m_sms.v as top, targets um-45k, and programs with fujprog/dfu-util. Python files from esp32/osd/ must be uploaded separately to the ESP32 (MicroPython) side. No testbenches found in this repo (find . -iname "*tb*.v" and directory listing show none; .claude/memory/catalogue.tsv records “none found” for tests, confirmed).
Reuse notes
| Block | Path | Top module | Language | Vendor primitives | License |
|---|---|---|---|---|---|
| OSD video overlay (windowing) | src/osd/osd.v | osd | Verilog | none | no header found |
| OSD SPI text-window controller | src/osd/spi_osd.v | spi_osd | Verilog | none | no header found |
| Generic SPI R/W BRAM slave | src/osd/spirw_slave_v.v | spirw_slave_v | Verilog | none | BSD (// AUTHOR=EMARD / // LICENSE=BSD) |
| SPI RAM slave + debounced BTN IRQ | src/osd/spi_ram_btn.v | spi_ram_btn | Verilog | none | BSD (// AUTHOR=EMARD / // LICENSE=BSD) |
| ESP32 MicroPython OSD client (generic loader) | esp32/osd/osd.py | class osd | MicroPython | n/a | BSD (# AUTHOR=EMARD / # LICENSE=BSD) |
| TV80 Z80-compatible CPU | src/Z80/tv80_core.v + tv80n.v/tv80_mcode.v/tv80_reg.v/tv80_alu.v | tv80n | Verilog | none | MIT (Guy Hutchison 2004, opencores; based on Daniel Wallner’s VHDL T80) |
| SDRAM controller (MiST-board port) | src/sdram.v | sdram | Verilog | none | GPL-3.0-or-later (Till Harbaum, “MiST board adaptation of Ludde’s NES core”) |
| SPI ST7789 display core | src/spi_display/lcd_video.v | lcd_video | Verilog | none | BSD (// AUTHORS=EMARD,MMICKO and Lawrie Griffiths) |
| Parametric ECP5 PLL | src/lattice/ecp5pll.sv | ecp5pll | SystemVerilog | EHXPLLL | BSD (// (c)EMARD) |
| SN76489 PSG | src/sn76489.v | sn76489 | Verilog | none | no header found |
Reuse guidance for the OSD stack (the primary reason this repo is catalogued): spirw_slave_v.v is a generic clocked SPI slave exposing rd/wr/addr[c_addr_bits-1:0]/data_in/data_out onto a byte-addressed bus (protocol: 00 <addr_hi> <addr_lo> <data...> to write, `01