FABulous is a parametric FPGA fabric generator. It takes a fabric definition (CSV or YAML), builds an in-memory fabric model, and emits the HDL, nextpnr chip database, synthesis scripts, geometry, and bitstream needed to target that fabric.
config (CSV/YAML) β fabric model β HDL + chip DB + synth scripts
user design β Yosys (synth) β nextpnr (P&R) β FASM β bitstream
- Python deps:
uv. Runuv syncafter pulling; add deps withuv add(--group devfor dev-only). Never hand-edituv.lock. Run commands asuv run <cmd>or activate.venv/. - EDA toolchain: Nix.
flake.nixpins Yosys, nextpnr, OpenROAD, GHDL, Librelane, etc. The GDS / ASIC flow (anything undertests/gds_flow_test,librelane_plugin_fabulous, orfabulous/fabric_generator/gds_generator) requires the Nix shell βnix developfirst, when inside the shell you will not needuv. Don't try to substitute system installs of these tools; CI uses Nix and version drift will burn you. - Tasks:
Taskfile.ymlis the canonical runner. The ones you'll use most:task test(forwards args after--; pass--runslowfor slow tests),task ci(run before pushing),task smoke-test(end-to-end).task --listfor the rest. - Pre-commit is enforced (ruff, docstring linting, dependency hygiene, etc.). If a hook fails, fix the cause β don't
--no-verify.
Rules that make changes feel native. Follow them even when a tool's defaults disagree.
- Python 3.12+. Built-in generics only (
list[int],X | None); nevertyping.List/Optional. - Paths:
pathlib.Path. Neveros.pathor string concatenation. - Logging:
loguru(from loguru import logger). Notprint, not stdliblogging. - Docstrings: NumPy style β
pydoclint+interrogate(β₯95% coverage) enforce this. - Inline code in docstrings/comments: We use MyST for documentation. We use single "`" for raw text and "_" for italic.
- Naming: this repo right now uses
camelCasefor variables/functions,PascalCasefor classes,CONSTANT_CASEfor module constants. Use snake case for all newly created methods and variables as we slow transition to meet PEP 8. - Formatting (line length, quotes, etc.) is whatever
ruff formatproduces.
- No fallbacks, no silent retries. Surface failures with clear errors. A stack trace beats a wrong result.
- Fix root causes. If a fix doesn't change behavior the way you expected, stop and re-analyze before patching again.
- Validate at boundaries, trust internals. Defensive checks belong where untrusted input enters (parsers, CLI, external tools).
- No premature abstraction. Three similar lines beat a speculative helper. Ship the smallest correct change.
- Clear over clever. Loops over unreadable comprehensions; parsers over paragraph-long regexes.
- Comments explain why, not what. Reserve them for hidden constraints, non-obvious invariants, bug-specific workarounds. Don't reference task numbers or callers β that rots.
- Pydantic for runtime-validated config (
fabulous_settings.py); dataclasses for the fabric model β keep them light, no heavy logic.
pytest+pytest-mock(use themockerfixture; don't importunittest.mock).- Mark slow tests
@pytest.mark.slowso defaulttask teststays fast. - Prefer real in-memory fabric fixtures over heavy mocks.
cocotbis available for RTL-level verification, not for Python logic.- Reuse the fixtures in
tests/conftest.pybefore writing new ones. - Parameterize tests with
pytest.mark.parametrizeso a new case is one more entry. - You can run
task test -- -p8to run the test in parallel with 8 thread.
- Don't edit the symlinked agent files (
CLAUDE.md,.cursorrules,.github/copilot-instructions.md) β editAGENTS.md. - Put new modules in an existing
fabulous/subpackage; add a new subpackage or top-level directory only when none fits. - When in doubt, read
Taskfile.yml,pyproject.toml, andtests/conftest.pyβ they encode the live conventions. The Sphinx docs underdocs/cover user-facing flow.