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_arch generic. Top entity f32c_core (rtl/cpu/f32c_core.vhd), 1794 lines, optional branch predictor, direct-mapped I/D caches, exception handling, single-step debug port. Per README.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-built TOP_MODULE_FILE from rtl/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, top top_ulx3s_12f_xram_sdram_selftest.vhd, constraints ulx3s_options/ulx3s_v20_selftest.lpf): this is the design behind the f32c/ bitstream shipped in emard/ulx3s-bin, confirmed by matching project name f32c_selftest_2ws_89mhz in rtl/proj/lattice/ulx3s/xram_sdram_selftest/makefile.
  • BRAM-only SoC (bram/, Diamond .ldf project 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/, per rtl/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/reset plus a C_clk_freq generic 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 with ecppll/nextpnr equivalents.
  • DVI/TMDS: vga2dvid.vhd itself is pure behavioral VHDL and portable; only the DDR output stage (ddr_dvid_out_se.vhd) is ECP5-specific (ODDRX1F via ddr_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_refresh generics), not ULX3S-pin-specific — the multi-port arbiter (C_ports generic) is the part worth lifting for a new design.
  • USB device: usb_serial.vhd needs 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/* in rtl/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 as rtl/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/.xcf only, no makefile-driven TOP_MODULE_FILE found) — not identified in this review.
  • xram_acram_emu_segpdi/makefile exists but was not inspected in depth (AXI-CRAM emulation SoC, purpose inferred from directory name only).
  • Whether spdif_tx.vhd and usb_serial.vhd (no per-file license header) are actually intended to fall under the repo BSD-2 LICENSE, or simply lack a header by omission, is unconfirmed — treat as BSD-2 by default per repo LICENSE but 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.md documents the CPU-to-SoC bus protocol in detail but was only skimmed; useful reading before wiring a new peripheral to f32c_core.

Back to top

Generated from the JSON in data/ by the scripts in .claude/skills. Facts cite the upstream file at the reviewed commit; verify before reuse.

This site uses Just the Docs, a documentation theme for Jekyll.