Native digital HDMI output for the TRS-80 Color Computer 2, on a ~$30 Tang Nano 9K (Gowin GW1NR-9). No analogue conversion anywhere in the path.
The hardware is an interposer that sits in the MC6847 VDG socket with the original chip piggybacked on top. The real 6847 keeps running and keeps driving SAM and PIA timing, so composite and RF still work and nothing on the motherboard changes behaviour — the FPGA only listens. A capture engine snoops the socket's byte stream into a shadow RAM, and a second 6847 core inside the FPGA replays it, rendering to 720×480p59.94 over HDMI with 48 kHz audio.
Snooping a 40-year-old NMOS bus through an interposer is the hard part. The signals churn for hundreds of nanoseconds around every fetch, the data bus is only valid for a fraction of each cycle, and every delay drifts with die temperature. The capture engine is therefore clock-slaved: the CoCo's own 3.58 MHz CLK is the single source of truth, a counter slaved to it predicts every fetch slot, and the noisy strobes (DA0, HS) are never triggers — only heavily-filtered phase references, classified against the observed clock race so that per-unit and thermal delay variation cannot move the sampling grid. Nothing in the design assumes absolute board timing; phase is acquired from the machine itself and tracked continuously.
The display side runs a genuine MC6847 core as the renderer, fed from the shadow RAM, permanently phase-locked to a crystal-exact HDMI raster. Rate mismatch between the CoCo's ~59.92 Hz and HDMI's 59.94 Hz is absorbed by four rotating field slots (a whole field repeats or drops about once a minute — no tearing).
A diagnostic overlay (toggled by a button) renders the capture's live view of the bus at the top of the screen: a raw DA0 oscillogram and the phase detector's state, so timing problems can be debugged from a photograph of the display.
Makefile Build entry points for both toolchains (see Usage).
BOM.md Parts list, with Mouser numbers.
gerbers/ Gerber files for PCB manufacturing.
schematics/ Schematics.
rtl/ Gateware sources.
coco_hdmi_top.v Top level: clocks, buttons, LEDs, overlay, HDMI.
vdg_snoop.v The capture engine (filters, phase lock, commits).
vdg_replay.v Raster-slaved MC6847 replay engine.
pixel_pipe.v Palette, artifact colouring, scan conversion.
hdmi_wrap.sv HDMI encoder wrapper (720x480p59.94, audio).
audio_mcp3201.v Cassette/sound ADC capture.
coco_hdmi.cst Pin constraints. coco_hdmi.sdc Timing constraints.
vendor/ Third-party cores (hdl-util HDMI, MC6847), with
local patches kept as in-tree diffs.
gowin/ PLL and primitive wrappers.
sim/ Full-system simulation.
coco_socket_model.v Behavioural CoCo socket: realistic bus noise,
churn, runts, ripple, thermal delay sweeps.
tb_coco_hdmi.sv Testbench: renders frames, byte-exact shadow
compare, golden-image compare.
golden/ Blessed reference frames.
scripts/ build.sh (open flow), build_vendor.sh + vendor.tcl
(Gowin flow), sim.sh (suite), pin_inputs.py.
Everything is driven by make (see make help):
make vendor Build with Gowin EDA -- the SIGNOFF flow.
make open Build with yosys/nextpnr/apicula (see caveat below).
make sim Run the 31-case simulation suite in parallel and
compare every rendered frame against sim/golden.
make check vendor + sim together: the full qualification.
make flash Write the vendor bitstream to flash (persistent).
make ram Load it into SRAM instead (volatile).
make clean
Two flows are supported, and they are not equivalent:
- Gowin EDA (
make vendor) is the signoff flow. The build is gated on the vendor's static timing analysis at silicon corners: zero setup and zero hold violations, verified from the timing report, plus artifact checks on the bitstream and pin configuration. The free Education edition suffices; Gowin's Standard edition license is also free. - The open flow (
make open) builds real bitstreams but cannot sign them off. nextpnr's timing model for this part measured ~1.4× optimistic against vendor corner STA on this very design, and it performs no hold analysis.build.shtherefore applies a derated Fmax gate calibrated from that cross-tool data. As of this writing no placement seed clears the honest bar: an open-flow bitstream may run fine on many boards at room temperature, but is a candidate, not a guarantee. Improving this (placer options, more RTL slack, upstreaming the timing data to apicula/nextpnr) is a welcome area for contribution.
Simulation needs Verilator (the oss-cad-suite works); the suite includes stress cases derived from real-hardware failures — bus churn, validated noise excursions, clock runts, address ripple, mode-line noise, and thermal delay sweeps on DA0 and HS.
The board's LEDs carry a diagnostic map (lock, edge-miss, stale, burst, and fake-edge detectors), and the on-screen overlay is a button-press away. Every bitstream is a candidate until it has run on real hardware: simulation and STA gate what they can, but only the bench verifies.
Bench-validated on real hardware: correct boots across power cycles, 30+ minutes of continuous operation without degradation, thermal drift of the VDG's output delays handled by design.
- v0.4 - tested. This is the revision to build. Adds JP3, a jumper that can route the 6847's clock through the FPGA. No firmware uses that yet, so leave JP3 on 1-2 (socket pass-through).
- v0.1 - tested, and the revision demoed in the youtube video. Same board without JP3.
See BOM.md for the parts list. The board is entirely through-hole.
- Does not support Coco-3. Tested only on Coco-2.
- Tested only with non-T1 6847. There is a jumper for T1, but its correctness is unverified
- Board fit verified only in Scott's Coco-2. Different Coco variants have 6847 and RF modulator in different positions.
- Check github release page at https://github.com/sbelectronics/coco-hdmi/releases for prebuilt firmware
- Download firmware and flash with
openFPGALoader -b tangnano9k -f <firmware_filename>
This project is licensed under the Apache License 2.0; see LICENSE.
Vendored third-party cores keep their own licenses: rtl/vendor/hdmi is
MIT/Apache-2.0 dual-licensed, and rtl/vendor/mc6847 comes from
hoglet67/AtomGodilVideo under Apache-2.0. Both carry a diff of the local
changes; the MC6847 tree also keeps a pristine upstream copy beside it.
