One-shot installer for the LogiSmith open-source FPGA toolchain on Ubuntu (native or WSL2) or Arch Linux. It automates the manual installation guide and verifies the result with an end-to-end build.
On Windows, wsl-setup.ps1 prepares the WSL 2 distro first
(versions, USB/IP + FTDI kernel modules) and then runs install.sh inside it —
see Windows (WSL 2).
Supports Ubuntu and Arch Linux; Xilinx (xc7) only. This script is a stopgap — the plan is to migrate to Nix, and to generalise beyond Xilinx. See DECISIONS.md.
Everything Anvil needs, at the fixed paths anvil.py expects:
| Path | Component |
|---|---|
~/opt/anvil |
Anvil CLI |
~/opt/sv2v/sv2v |
SystemVerilog → Verilog converter |
~/miniconda3 |
Conda + the xc7 F4PGA environment |
~/opt/f4pga/xc7 |
F4PGA architecture definitions (Artix-7) |
~/f4pga-examples |
F4PGA examples (common.mk) |
~/opt/verif |
venv with cocotb + forastero (simulation) |
Plus the F4PGA carry-chain patch, and optionally the RISC-V toolchain,
openFPGALoader (board programming), and simulation tools — Verilator (built from
source) with cocotb + forastero (--no-sim to skip).
git clone https://github.com/LogiSmith/toolchain-setup.git
cd toolchain-setup
./install.shThe package-manager step auto-detects pacman vs apt — no flag needed to pick
between Ubuntu and Arch.
Or in one line:
curl -fsSL https://raw.githubusercontent.com/LogiSmith/toolchain-setup/main/install.sh | bashOn Windows, don't start here — start with the WSL 2 helper below, which prepares the distro and then runs this installer inside it.
| Flag | Effect |
|---|---|
--minimal |
Skip optional tools (RISC-V, openFPGALoader/board, simulation) |
--no-board |
Skip only openFPGALoader + udev |
--no-sim |
Skip simulation tools (Verilator + cocotb + forastero) |
--no-test |
Skip the final integration test |
--skip-apt |
Skip the package-manager steps (deps already present) |
-h, --help |
Show help |
The installer is idempotent — re-running skips anything already installed,
and is update-aware: it checks out the latest published Anvil release
(GitHub Releases, not main) and picks up any bumped dependency versions. So
re-running it (or anvil update) updates the whole toolchain, not just one part.
Dependency pins are the "lockfile" at the top of install.sh; Anvil itself
tracks its latest release — see VERSIONS.md.
wsl-setup.ps1 is the Windows-side helper, not a second
installer: it prepares WSL and then calls install.sh inside it.
From a normal (non-admin) PowerShell — one line, no clone needed:
irm https://raw.githubusercontent.com/LogiSmith/toolchain-setup/main/wsl-setup.ps1 -OutFile "$env:TEMP\wsl-setup.ps1"; powershell -ExecutionPolicy Bypass -File "$env:TEMP\wsl-setup.ps1"Not irm | iex: exit inside iex ends the whole PowerShell session, so a failed
check would close the window along with the error explaining how to fix it.
Or from a clone:
.\wsl-setup.ps1It checks, in order:
- WSL present — Windows build ≥ 19041 and WSL installed; otherwise it prints
the
wsl --installinstructions (admin + reboot) and stops. - WSL and kernel versions — at least WSL 2.7.13.0 and kernel 6.18.33.2
(the versions shipping
vhci-hcd+ftdi_sio); otherwise it stops and asks forwsl --update. Akernel=/kernelModules=line in%USERPROFILE%\.wslconfigis allowed but reported as a custom (non-Microsoft) kernel — commented-out lines are ignored. Also warns on low disk space and missingusbipd-win. - Distro name — default
anvil; if it already exists you can reuse it or pick another name. Nothing is ever deleted. - Create the distro —
wsl --install --name <name> --no-launch, then runs the distro's own first-run setup ([oobe] commandfrom/etc/wsl-distribution.conf) directly. WSL's own prompt asks for the UNIX username and password — the script neither sets nor reads them — and control returns here by itself.--no-launchmatters: a plain install ends by dropping you into a Linux shell, where the script appears to hang while it waits for you to typeexit. 4b. Command channel self-test — a failsafe. Everything the script does inside the distro depends onwsl --execpassing a command through unchanged, so it first proves that still holds: plain arguments arrive intact, quoting and shell expansion survive, and exit codes come back. If a future WSL changes this, the script stops here — before touching anything — and tells you to pin WSL back to the last verified release (with thewingetcommands to do it). - Default user — must be non-root (
install.shrefuses root). WSL's default user is a setting separate from the account, so a distro can hold a perfectly good account and still start as root: if that happens the existing uid-1000 account is adopted (wsl --manage --set-default-user), and only if there is none does it offer to create one. - Kernel modules — the check that actually decides. Reports the running
kernel (
uname -r), which is not always the onewsl --versionreports: a running VM keeps the kernel it booted with (custom, or stale from before an update) until every distro stops, so it warns and points atwsl --shutdown. Then loadsvhci-hcd+ftdi_sioand accepts them fromlsmodormodules.builtin— a kernel with the drivers built in (=y) never shows them inlsmod. On failure it reports whether/lib/modules/<running kernel>even exists in the distro (the usual cause with a custom kernel) and gives the fixes: drop the custom kernel andwsl --shutdown,wsl --update, or build a kernel withCONFIG_USBIP_VHCI_HCD+CONFIG_USB_SERIAL_FTDI_SIOand install its modules. On success it persists them in/etc/modules-load.d. - Toolchain — green "ready" line, 5 s pause, then
install.shinside the distro.
| Flag | Effect |
|---|---|
-Name <name> |
Distro name (default anvil) |
-Image <image> |
Image from wsl --list --online (default Ubuntu-24.04) |
-Reuse |
Reuse the distro if it exists, without asking |
-SkipAnvil |
Prepare/verify WSL only; don't run install.sh |
-SkipDriverCheck |
Skip the vhci-hcd/ftdi_sio check (no board programming) |
-AllowOlder |
Accept a WSL/kernel older than the tested pins (warn instead of stop) |
-InstallArgs '<flags>' |
Flags passed through to install.sh, e.g. '--no-test' |
Re-running is safe: with -Reuse it re-checks the distro and re-runs the
(idempotent) installer.
When Microsoft ships a new WSL. The two version numbers are pins (see VERSIONS.md), recording what was tested — not a claim about what works. A newer WSL passes them, so a normal Microsoft update changes nothing. Two cases can still bite:
- Microsoft drops the modules again (this is why
bzImage-ftdi-style custom kernels exist). The version check would pass, but step 6 fails and tells you to update or build a kernel — the module check, not the version number, is the authority on whether the board works. - Microsoft renumbers downwards (e.g. back to a 6.6 LTS kernel, numerically
lower than 6.18 but perfectly fine). The pin would stop you wrongly; re-run with
-AllowOlderto drop it to a warning and let step 6 decide, and bump the pins in the script once the new set is verified.
Only wsl --install --name, which needs WSL 2.4.4+, is a hard requirement that
-AllowOlder cannot waive.
If the change is not in the versions but in how WSL runs commands, the step-4b self-test catches it and prints the way back to the tested release:
# ADMINISTRATOR PowerShell
wsl --shutdown
winget uninstall --id Microsoft.WSL --exact
winget install --id Microsoft.WSL --exact --version 2.7.13 --accept-package-agreements
winget pin add --id Microsoft.WSL --exact # stop it updating again
wsl --version # confirm 2.7.13.0(winget show --id Microsoft.WSL --exact --versions lists what is available;
the installers are also at https://github.com/microsoft/WSL/releases.)
To program a board from WSL you also need usbipd-win
on the Windows side (winget install --exact dorssel.usbipd-win), then
usbipd attach --wsl --busid <BUSID> once per session.
If PowerShell blocks the script (execution policy), run it as:
powershell -ExecutionPolicy Bypass -File .\wsl-setup.ps1anvil update # from anywhere — re-runs this installer
# or directly:
curl -fsSL https://raw.githubusercontent.com/LogiSmith/toolchain-setup/main/install.sh | bashanvil update passes flags through, e.g. anvil update --no-test.
After installing, it runs anvil doctor, then — using the installed anvil
command (resolved from the alias in ~/.bashrc) — scaffolds a uart-hello
project under ~/opt, builds it end to end, verifies a bitstream was produced,
and deletes the project. Fails loudly if anything breaks. Skip with --no-test.
Released under the MIT License. © 2026 LogiSmith.