f32c retargetable RISC-V/MIPS SoC (f32c__f32c)
| Field | Value |
|---|---|
| Upstream | https://github.com/f32c/f32c |
| Reviewed at | 7dbf56d (upstream date 2026-09-22), reviewed 2026-09-28 |
| License | BSD-2-Clause, repo-wide (LICENSE, Copyright Marko Zec / EMARD). Several individual rtl/soc/* files carry a different permissive header from their original author — see Reuse notes |
| HDL / framework | VHDL (bulk of rtl/cpu, rtl/soc, rtl/lattice), a handful of Verilog leaf modules (i2s_v.v, vgahdmif_v.v, Diamond-SCUBA-generated PLL .v/.vhd netlists), C runtime/examples under src/ |
| Toolchain | diamond for every working ULX3S target (rtl/proj/lattice/include/scripts/diamond_main.mk + universal_make/files.mk). An open-source path exists as generic infra (rtl/lattice/include/scripts/ulx3s_trellis.mk, yosys+nextpnr-ecp5+ecppack), but the two ULX3S trellis attempts are explicitly broken: rtl/proj/lattice/ulx3s/bram_trellis/README.md and xram_sdram_normal_trellis/README.md both say “# Not working / This doesn’t compile yet, just an attempt” |
| Programmer | ujprog/fujprog (UJPROG ?= fujprog, rtl/proj/lattice/include/scripts/diamond_main.mk:102); FleaFPGA-JTAG and OpenOCD targets also defined in the same makefile |
| Target FPGA(s) | 12F / 25F / 45F / 85F (top files and makefiles per size, e.g. top_ulx3s_12f_xram_sdram.vhd, top_ulx3s_85f_xram_acram_emu_vector.vhd) |
| Board revision(s) | v1.7-patch (ulx3s_v17patch.lpf), v2.0/v3.0.x (ulx3s_v20.lpf + ulx3s_options/ulx3s_v20_{segpdi,selftest,uncompressed,jtagflash}.lpf), v3.1.6+ (ulx3s_options/ulx3s_v316.lpf present but commented out / not the active CONSTRAINTS line in any current variant’s makefile, e.g. xram_sdram_normal/makefile) |
| Activity | Last commit 2026-09-22 (git log -1 --format=%cs); 5912 commits since 2010-02-27 per .claude/memory/history.tsv (clone is shallow, --depth 1, so commit count is not independently verifiable here) |
Scope of this page: the f32c CPU core and the ULX3S SoC targets under rtl/proj/lattice/ulx3s/, plus the SoC peripherals worth reusing elsewhere. The repo also targets Xilinx, Altera/Intel and several non-ULX3S Lattice boards (rtl/xilinx, rtl/altera, rtl/lattice/{brevia,ffm-lfe5u, fleafpga_ohm,fleafpga_uno,icoboard_beta,kondor_ax,mmm_v4r0,sparrowhawk, ulx2s}) and a large software tree (src/: C runtime, compiler, bootloader, examples, benchmarks) — both are out of scope here and not reviewed.
What the gateware does
- f32c CPU core: retargetable, scalar, pipelined 32-bit soft processor that executes a subset of either RISC-V (RV32) or MIPS (MI32), selected by the
C_archgeneric. Top entityf32c_core(rtl/cpu/f32c_core.vhd), 1794 lines, optional branch predictor, direct-mapped I/D caches, exception handling, single-step debug port. PerREADME.md: 3.3 CoreMark/MHz from BRAM, up to ~185 MHz Fmax on 6-input-LUT FPGAs (unverified: README claim, not confirmed for ECP5/ULX3S specifically here). - ULX3S SoC integration (
rtl/proj/lattice/ulx3s/, 17 build directories): each variant picks one pre-builtTOP_MODULE_FILEfromrtl/lattice/ulx3s/top/*.vhd(28 top files) that wires the CPU to a peripheral set — video output flavor, SDRAM vs BRAM, self-test, etc. See Structure below for the mapping. - SDRAM-backed general-purpose SoC (
xram_sdram_normal,xram_sdram_synth,xram_sdram_text,xram_sdram_tvfm,xram_sdram_433,xram_sdram_lvds,xram_sdram_noflash): CPU + external SDRAM + video + UART + SPI flash/SD, the general-purpose configuration people build for. - Board self-test (
xram_sdram_selftest, toptop_ulx3s_12f_xram_sdram_selftest.vhd, constraintsulx3s_options/ulx3s_v20_selftest.lpf): this is the design behind thef32c/bitstream shipped inemard/ulx3s-bin, confirmed by matching project namef32c_selftest_2ws_89mhzinrtl/proj/lattice/ulx3s/xram_sdram_selftest/makefile. - BRAM-only SoC (
bram/, Diamond.ldfproject only — no local makefile/top source in this pruned checkout;bram_trellis/is the (broken) open-flow attempt at the same target). - SoC peripheral library (
rtl/soc/, perrtl/soc/README.md): multiport SRAM/SDRAM/AXI/DDR3 memory controllers, floating-point vector processor, 2D video accelerator (windows/sprites/textmode/scroll/blitter) with HDMI/VGA/composite/LED-strip output, 16-bit stereo PCM DMA audio, a 128-voice tonewheel synth, SPDIF and analog PWM audio out, an 88–108 MHz FM/RDS transmitter, a 433.92 MHz ASK transmitter, a PID motor controller, timer/GPIO/SPI/UART, USB 1.1 device (serial/CDC), PS/2, and an RTC.
Structure
rtl/cpu/ f32c_core.vhd (CPU), idecode_{mi32,rv32}.vhd, f32c_cache.vhd, debug.vhd
rtl/soc/ peripheral library (see Reuse notes table)
rtl/lattice/ulx3s/top/ 28 top-level .vhd files, one per SoC configuration/FPGA size
rtl/lattice/ulx3s/clocks/ Diamond-SCUBA-generated PLL netlists (ECP5U library), one per top file's clock plan
rtl/proj/lattice/constraints/ ulx3s_v20.lpf, ulx3s_v17patch.lpf
rtl/proj/lattice/constraints/ulx3s_options/ segpdi / selftest / uncompressed / jtagflash / v316 variants
rtl/proj/lattice/ulx3s/ 17 build dirs, each a makefile (or .ldf) selecting a top file + constraints
rtl/proj/lattice/ulx3s/universal_make/ files.mk shared by all non-trellis ulx3s makefiles
rtl/proj/lattice/include/scripts/ diamond_main.mk / diamond_path.mk (working flow); ulx3s_trellis.mk (generic open-flow infra, unused by any working ulx3s target)
src/ C runtime, compiler, bootloader, examples (out of scope)
17 rtl/proj/lattice/ulx3s/ build directories and what each configures (makefile PROJECT/TOP_MODULE_FILE, verified per-directory):
| Directory | Project name | Top file | Notes |
|---|---|---|---|
xram_sdram_normal | f32c_pix8bpp640x480_100mhz | top_ulx3s_12f_xram_sdram.vhd | general-purpose default |
xram_sdram_433 | f32c_433_93mhz | top_ulx3s_12f_xram_sdram_433.vhd | 433.92 MHz ASK radio |
xram_sdram_lvds | f32c_pix8bpp640x480_100mhz | top_ulx3s_12f_xram_sdram_lvds.vhd | LVDS video variant |
xram_sdram_noflash | f32c_noflash_100mhz | top_ulx3s_12f_xram_sdram_noflash.vhd | no SPI flash access |
xram_sdram_selftest | f32c_selftest_2ws_89mhz | top_ulx3s_12f_xram_sdram_selftest.vhd | board self-test (matches ulx3s-bin’s f32c/) |
xram_sdram_synth | f32c_synth_100mhz | top_ulx3s_12f_xram_sdram_synth.vhd | tonewheel synth demo |
xram_sdram_text | f32c_txt100x60_100mhz | top_ulx3s_12f_xram_sdram_text.vhd | text-mode video |
xram_sdram_tvfm | f32c_tvfm_81mhz | top_ulx3s_12f_xram_sdram_tv.vhd | composite TV + FM demo |
bram | (Diamond .ldf only) | unknown (top source not present in this pruned checkout) | BRAM-only SoC |
bram_trellis | f32c | (missing FILES= in Makefile) | broken open-flow attempt |
xram_sdram_normal_trellis | f32c | (missing FILES= in Makefile) | broken open-flow attempt |
xram_sdram_12f_v2.0_c2_noscripts | unknown | unknown | project.ldf only, .xcf programmer files for 3 flash chips |
sdram_dv, sdram_emu, sdram_min | unknown | unknown | .ldf-only Diamond projects, no source present in this pruned checkout |
xram_acram_emu_segpdi | unknown | unknown | AXI-CRAM emulation variant, makefile present but not inspected in depth |
universal_make | — | — | shared files.mk/makefile included by the working variants, not a build target itself |
Several directories (bram, sdram_dv, sdram_emu, sdram_min, xram_sdram_12f_v2.0_c2_noscripts) ship only a Diamond .ldf/.xcf project file with no companion makefile driving TOP_MODULE_FILE; their exact top module is unknown from this review — Diamond project files were not parsed.
How to build
Not run (per task scope: no make/build). From reading the makefiles:
cd rtl/proj/lattice/ulx3s/xram_sdram_normal
make # Diamond synth+PAR via diamond_main.mk, needs $DIAMOND_BASE
make prog # fujprog <bitstream>.bit, load over US1 JTAG
make flash # fujprog -j flash <bitstream>.bit, permanent
Requires Lattice Diamond installed and DIAMOND_BASE pointing at it (rtl/proj/lattice/include/scripts/diamond_path.mk, not read in detail). FPGA_SIZE/CONSTRAINTS/TOP_MODULE_FILE are chosen per variant inside each makefile (see table above); switching board revision means uncommenting a different CONSTRAINTS line.
The open-flow path (bram_trellis, xram_sdram_normal_trellis) uses yosys -m ghdl -q synth.ys + nextpnr-ecp5 --router router2 + ecppack, but both are marked non-working by their own README.md.
Reuse notes
Reusable blocks (path relative to original_sources/f32c__f32c/, commit 7dbf56d). License column is the individual file’s own header, which in several cases differs from the repo-wide BSD-2-Clause LICENSE — check before reusing.
| Block | Path | Top module | Language | Vendor primitives | License (per-file) |
|---|---|---|---|---|---|
| f32c CPU core | rtl/cpu/f32c_core.vhd | f32c_core | VHDL | none | BSD-2 (Marko Zec, file header) |
| SPDIF audio out | rtl/soc/spdif_tx.vhd | spdif_tx | VHDL | none | no per-file header found; falls back to repo BSD-2 |
| FM/RDS transmitter | rtl/soc/fm/fm.vhd (+ fmgen.vhd, rds.vhd, bram_rds.vhd, fir.vhd, lowpass.vhd, message.vhd) | fm | VHDL | none | BSD-2 (Davor Jadrijevic, file header) |
| USB device (CDC-ACM serial) | rtl/soc/usb_serial/usb_serial.vhd (+ usb_control.vhd, usb_init.vhd, usb_packet.vhd, usb_transact.vhd, usbsio.vhd) | usb_serial | VHDL | none (needs external UTMI-style PHY) | no per-file header found; falls back to repo BSD-2 |
| USB 1.1 soft PHY | rtl/soc/usb11_phy/usb_phy.vhd, usb_rx_phy_{48MHz,60MHz,emard}.vhd, usb_tx_phy.vhd | usb_phy | VHDL (converted from Verilog by Martin Neumann) | none | OpenCores permissive, Copyright (C) 2000-2002 Rudolf Usselmann (www.asics.ws) — “may be used and distributed without restriction provided copyright notice is retained”, NOT the repo’s BSD-2 text |
| SDRAM controller | rtl/soc/sdram.vhd, sdram32.vhd, sdram_mz.vhd | sdram / sdram32 / sdram_mz | VHDL | none (generic SDR SDRAM command sequencing) | MIT, Copyright (c) 2013 Mike Field <hamster@snap.net.nz> — catalogue.tsv’s “(BSD-2)” tag for this block is wrong |
| DVI/TMDS video | rtl/soc/vgahdmi/vga2dvid.vhd, TMDS_encoder.vhd | vga2dvid | VHDL | serializer via ddr_dvid_out_se.vhd → rtl/lattice/chip/ecp5u/ddr_out.vhd (ECP5 ODDRX1F) | MIT, Copyright (c) 2012 Mike Field for vga2dvid.vhd; ddr_dvid_out_se.vhd/ddr_out.vhd are BSD (Emard, -- LICENSE=BSD header) |
| Composite video (PAL/CVBS) | rtl/soc/cvbs.vhd | cvbs | VHDL | none (sigma-delta DAC) | BSD-2 (Marko Zec, 2013, file header) |
| PS/2 keyboard/mouse | rtl/soc/ps2.vhd | ps2 | VHDL | none | MIT, Copyright (c) 2015 Ken Jordan — not BSD-2 |
| I2S audio | rtl/soc/i2s.vhd (VHDL wrapper) + i2s_v.v (Verilog core) | i2s | VHDL+Verilog | none | BSD (Emard, -- LICENSE=BSD header) |
| Tonewheel synth | rtl/soc/synth.vhd | synth | VHDL | none | BSD (Davor Jadrijevic, -- LICENSE=BSD header) |
| RTC | rtl/soc/rtc.vhd | rtc | VHDL | none | BSD-2 (Marko Zec, 2023, file header) |
Notes on dropping these into a new ULX3S design:
- Clock/reset: most blocks take a plain
clk/resetplus aC_clk_freqgeneric in Hz (e.g.spdif_tx,ps2,cvbs) — any system clock works, no PLL dependency baked in. The CPU core (f32c_core) is itself clock-generic; the SoC top files instantiate Diamond-SCUBA PLL netlists per configuration (rtl/lattice/ulx3s/clocks/*.vhd, e.g.clk_25_100_125_25.vhd, header: “VHDL netlist generated by SCUBA Diamond (64-bit) 3.7.0.96.1”) — these are Diamond-only IP (ECP5U primitive library), not portable to the open flow without regenerating withecppll/nextpnrequivalents. - DVI/TMDS:
vga2dvid.vhditself is pure behavioral VHDL and portable; only the DDR output stage (ddr_dvid_out_se.vhd) is ECP5-specific (ODDRX1Fviaddr_out), and that wrapper already targets ECP5U so it should carry over directly on another ULX3S design. - SDRAM controller: generic 16-bit SDR SDRAM interface (
sdram_address_width,sdram_column_bits,sdram_startup_cycles,cycles_per_refreshgenerics), not ULX3S-pin-specific — the multi-port arbiter (C_portsgeneric) is the part worth lifting for a new design. - USB device:
usb_serial.vhdneeds a UTMI-style 8-bit PHY; this repo supplies its own soft PHY (usb11_phy/, OpenCores-derived, see license caveat above) rather than a hard UTMI transceiver, so the pair reuses as a unit. - Pin/LPF reuse: this repo already targets ULX3S with
ulx3s_v20.lpf/ulx3s_v17patch.lpf/ulx3s_options/*inrtl/proj/lattice/constraints/, so pin names for a new ULX3S design built from these blocks should carry over directly; only clock-frequency generics need adjusting per target board revision. - The emard “universal make” pattern referenced in
.claude/memory/reusable-cores.md(trellis_main.mk/diamond_main.mk,FPGA_SIZE=12|25|45|85) is present here too asrtl/proj/lattice/include/scripts/diamond_main.mk/rtl/proj/lattice/ulx3s/universal_make/files.mk, confirming the “Diamond-only historical ports” line in that file for f32c.
Open questions
- Top module / source files for
bram,sdram_dv,sdram_emu,sdram_min,xram_sdram_12f_v2.0_c2_noscripts(Diamond.ldf/.xcfonly, no makefile-drivenTOP_MODULE_FILEfound) — not identified in this review. xram_acram_emu_segpdi/makefileexists but was not inspected in depth (AXI-CRAM emulation SoC, purpose inferred from directory name only).- Whether
spdif_tx.vhdandusb_serial.vhd(no per-file license header) are actually intended to fall under the repo BSD-2LICENSE, or simply lack a header by omission, is unconfirmed — treat as BSD-2 by default per repoLICENSEbut flag before redistributing standalone. - CPU performance claims in
README.md(CoreMark/MHz, Fmax) are unverified for the ULX3S/ECP5 target specifically — no benchmark run in this review. rtl/cpu/README.mddocuments the CPU-to-SoC bus protocol in detail but was only skimmed; useful reading before wiring a new peripheral tof32c_core.