lfpga
A package manager for FPGA development. Resolve, fetch, build and verify open IP cores from the registry, in one command. The registry is the pypi. lfpga is the pip.
pip install lfpga
The whole loop, in one session
$ pip install lfpga $ lfpga init my-soc $ lfpga add libfpga Found libfpga: https://github.com/libfpga/libfpga license MIT · verilog · ✓ lint, synth, testbench, formal $ lfpga install libfpga v0.5.0 a4ef4a3fa4 ✓ verified Wrote lfpga.lock and build/sources.f $ lfpga sim TB PASS: CRC-16/CCITT check = 0x29b1 ✓ simulation passed $ lfpga synth 17 LUT4s, 16 flip-flops ✓ synthesis succeeded
Why a package manager for FPGA is a first
You cannot just clone npm and swap the file extensions. Hardware breaks almost every assumption software package managers lean on, and getting those differences right is the whole point of lfpga.
There is no binary and no linking. A Python wheel is a compiled artifact you link against. An FPGA core is HDL source that gets elaborated together with your design on every build. So lfpga fetches source and vendors it, it never produces a binary. And there is no universal build: Verilog and VHDL are consumed by a dozen tools, each with its own file-list format, so lfpga emits a plain filelist they all understand and can drive the open ones directly.
Every package can be proven to build
This is the part no other FPGA tool offers. Each listing in the
registry can be run through an open toolchain that lints
it with Verilator, synthesizes it with Yosys, and runs its testbench with
Icarus. A core that passes earns badges and real resource numbers, and those
badges are earned by the toolchain, never self-declared. So
lfpga add can tell you a dependency actually builds before you
clone a single line.
How it works
lfpga asks the registry for a core's source repository, resolves a version
range against that repo's git tags, fetches and pins the exact commit into a
local cache, reads the repo's own manifest to know which files are the
synthesizable sources, checks for module-name collisions, and writes a
lockfile plus a build/sources.f filelist. From there it hands the
filelist to your flow or runs the build itself.
The commands, in detail
Add a core, badges and all
lfpga looks the core up in the registry and shows you what you are getting before it touches your disk.
$ lfpga add libfpga Found libfpga: https://github.com/libfpga/libfpga license MIT · verilog · ✓ lint, synth, testbench, formal Added 'libfpga' to libfpga.yaml. Run `lfpga install`.
Resolve versions against real git tags
Ask for a range and lfpga picks the highest matching tag the core actually
published. Carets, tildes, 1.x wildcards, comparators and exact
versions all work.
$ lfpga add serv@^1.2 $ lfpga install serv 1.4.0 7d9cde4e6c 18 files unverified
^1.2 resolved to 1.4.0, the newest 1.x
tag on the SERV repo. If nothing matches you get a clear error listing the
available versions, not a mystery.
Install, and pin it in a lockfile
Because HDL builds are source-and-elaborate with no ABI, reproducibility is
lockfile-first. Commit lfpga.lock: it pins the exact commit and
the exact build sources, so a build is identical across machines and years.
# lfpga.lock [[package]] name = "libfpga" version = "v0.5.0" rev = "a4ef4a3fa4ac1de6aa485baf3efc56f14a6df704" files = ["rtl/math/lfpga_crc.v"] verified = ["formal", "lint", "synth", "testbench"]
Simulate and synthesize, do not just fetch
This is where lfpga becomes a build tool. Point it at a testbench and it runs the simulation with Icarus or Verilator. Synthesis is one command too, and it reports the area.
$ lfpga sim TB PASS: CRC-16/CCITT check = 0x29b1 ✓ simulation passed $ lfpga synth 17 LUT4s, 16 flip-flops ✓ synthesis succeeded (top: lfpga_crc)
For Vivado or Quartus, lfpga sources gives you a
-f filelist to drop straight into your existing flow.
Catch collisions before they bite
Verilog has one global module namespace, so two cores that both define a
fifo will not elaborate together. lfpga scans the resolved
sources and tells you up front, instead of letting the build mis-compile.
! 1 module-name collision(s) (these will clash at elaboration):
module 'fifo': acme-fifo (rtl/fifo.v), other-lib (src/fifo.v)
Fix: pin different versions, modules-subselect, or drop one dependency.
Already using FuseSoC or Bender? Import it
lfpga import reads a FuseSoC .core or a
Bender.yml into your manifest, so an existing project adopts lfpga
in seconds.
$ lfpga import mycore.core
Imported 1 dependencies and 1 fileset(s) into libfpga.yaml.
The manifest
One file, libfpga.yaml, like Cargo.toml: it both declares your
dependencies and, if you publish, describes your own core.
# libfpga.yaml
name: my-soc
dependencies:
libfpga: "*" # latest default branch
picorv32: { rev: v1.0.3 } # pin a tag, branch or commit
libfpga-myhdl: { modules: [lfpga_mac] } # sub-select from a repo
private-mac: { git: "https://github.com/acme/mac.git" }Command reference
lfpga init [name] create a libfpga.yaml here lfpga add <pkg>[@ver] add a dependency (name, name@version, or a git URL) lfpga install resolve, pin (lfpga.lock), fetch, write build/sources.f lfpga list show the locked dependencies and their badges lfpga sources [--format] emit the assembled source list lfpga sim [--tool] run a simulation (Icarus / Verilator) lfpga synth synthesize with Yosys and report area lfpga import <file> import a FuseSoC .core or Bender.yml
How to contribute
lfpga and the registry are open, and there are three easy ways to help:
- List or claim your core. If you maintain an open FPGA
core, add it to the registry or claim an existing
listing by committing a small
libfpga.yamlto your repo. Claimed cores can run the toolchain and earn verification badges. - Hack on the tool. lfpga is a small, readable Python package on GitHub. Issues and pull requests are very welcome, especially around new emitters and tool backends.
- Shape the roadmap. Edalize backends for the vendor flows
(Vivado, Quartus) and a
lfpga publishcommand gated by the verification toolchain are next. Tell us what you need.