No description
  • Shell 55.3%
  • Dockerfile 44.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Paulo Ficagna 6090b642f2 Add --batch mode for agent job containers
No display, no capabilities or route out, rootless only; --usb passes just
the JTAG adapters. Used by the pi-docker --fpga broker.
2026-10-18 16:28:17 +02:00
.gitignore Add the RISC-V toolchain and record the licensed-IP traps 2026-08-18 14:07:27 +02:00
altera_docker.sh Add --batch mode for agent job containers 2026-10-18 16:28:17 +02:00
Arrow_USB_Programmer_2.5.1_linux64.zip Add the Arrow USB Programmer driver and the missing Nios II libraries 2026-08-18 10:52:48 +02:00
Dockerfile Add the Arrow USB Programmer driver and the missing Nios II libraries 2026-08-18 10:52:48 +02:00
Dockerfile.extras Add the RISC-V toolchain and record the licensed-IP traps 2026-08-18 14:07:27 +02:00
entrypoint.sh Add Questa-Intel FPGA Starter Edition to the dev environment 2026-07-18 16:04:37 +02:00
llm_section_context_recap.md initial commit 2025-08-25 14:41:39 +02:00
README.org Run Quartus on the rootless Docker daemon by default 2026-10-03 21:12:59 +02:00
xpack-riscv-none-elf-gcc-12.5.0-1-linux-x64.tar.gz.sha Add the RISC-V toolchain and record the licensed-IP traps 2026-08-18 14:07:27 +02:00

Prerequisites

Before you begin, ensure you have:

  • Docker installed and running on your Linux system.
  • Downloaded the following files and placed them in the same directory as your Dockerfile (all from the Quartus Prime Lite 22.1std download page):

    • QuartusLiteSetup-22.1std.0.915-linux.run
    • QuestaSetup-22.1std.0.915-linux.run
    • cyclone10lp-22.1std.0.915.qdz

Prefer the individual .run downloads over the "Complete Download" .tar: they add up to far less than the ~6 GB tar (which bundles every device family), and the Dockerfile references the .run filenames directly. Check that the downloaded filenames carry the same build number (0.915) as the COPY lines in the Dockerfile — if Intel has bumped the build, update the filenames there and in this README.

Fallback if an individual file is ever unavailable: the Complete .tar contains all of them under components/ — extract it somewhere temporary (NOT next to the Dockerfile, it would bloat the Docker build context), copy out the needed files, delete the rest.

Step 1: Save the Dockerfile & Prepare Your Folder

Save the updated Dockerfile code into a file named Dockerfile. Make sure the Quartus installer and device files are in this same directory. Your directory structure should look like this:

altera-dev-env/ ├── Dockerfile ├── entrypoint.sh ├── altera_docker.sh ├── QuartusLiteSetup-22.1std.0.915-linux.run ├── QuestaSetup-22.1std.0.915-linux.run └── cyclone10lp-22.1std.0.915.qdz

Step 2: Build the Docker Image

Navigate to the directory containing your Dockerfile and the installer files in a terminal. Run the following command to build the Docker image.

# Build the image, passing your user/group ID to avoid permission issues
sudo docker build \
  --no-cache \
  --build-arg UID=$(id -u) \
  --build-arg GID=$(id -g) \
  -t quartus-c10lp .

This build process will be much faster as it uses your local files instead of downloading them.

Step 3: Get the free Questa license (one time)

Quartus Lite itself needs no license, but two things do: Questa-Intel FPGA Starter Edition, and any licensed IP in a design (see Licensed IP below). Both are node-locked to a NIC MAC address (FlexLM host ID), so one invented MAC covers both and the same pinning trick works for each.

  1. Invent a locally-administered MAC address (second hex digit must be 2, 6, a, or e — e.g. 6e:df:de:44:ec:7e). Do NOT use a real interface's MAC: registering an invented one and pinning the container to it makes the license portable across machines (same trick as xilinx-dev-env).
  2. In the Intel FPGA Self-Service Licensing Center, choose "Sign up for Evaluation or Free Licenses" and select Questa-Intel FPGA Starter Edition (SW-QUESTA). When asked for a computer with a NIC ID, register your invented MAC without colons (6edfde44ec7e).
  3. Intel emails you a .dat license file. Place it at:
mkdir -p ~/dev/fpga/licenses
cp LR-xxxxxx_License.dat ~/dev/fpga/licenses/questa_license.dat
  1. Edit the two variables at the top of altera_docker.sh:

    • LICENSE_MAC — your invented MAC, formatted with colons. The launcher passes it as Docker's --mac-address so FlexLM always sees the licensed host ID (this is also why the container uses a bridge network instead of --net=host — Docker can only set a MAC on an interface it owns).
    • LICENSE_FILE — path to the license inside the container. The default /licenses/questa_license.dat already matches the copy step above.

Verify from inside the container (./altera_docker.sh -c):

vsim -c -do "quit"   # should start without a license error

Step 4: Run the Docker Container

Use the launcher — it handles X11 permission (xhost +local:docker), the license MAC/file, USB Blaster passthrough, and mounting the current directory at /workspace:

./altera_docker.sh          # Quartus GUI
./altera_docker.sh --cli    # bash shell with the tool environment sourced
./altera_docker.sh --sim    # Questa GUI (vsim)

Run it from your project directory — whatever directory you launch from becomes /workspace inside the container.

Rootless Docker (no sudo)

The launcher uses the rootless Docker daemon when its socket ($XDG_RUNTIME_DIR/docker.sock) is up and holds quartus-c10lp:latest; otherwise it falls back to the system daemon. The banner's Docker : line says which one ran. Under rootless the container runs as root, which maps to your host user, so files written to /workspace stay owned by you.

The image must exist in the rootless daemon's own store. Copy it from the system daemon through a file, not a pipe — a sudo prompt written to stdout corrupts a docker save | docker load stream:

sudo docker save -o quartus-c10lp.tar quartus-c10lp:latest
sudo chown $USER quartus-c10lp.tar
DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock docker load -i quartus-c10lp.tar
rm quartus-c10lp.tar

--rootful forces the system daemon. Use it for JTAG programming until USB passthrough is proven under rootless.

Note: xhost +local:docker is generally safe, but it allows any local container started by your user to connect to your display. Revoke it later with xhost -local:docker.

Step 5: Develop Inside the Container

After running ./altera_docker.sh --cli, your terminal prompt will change, indicating you are now inside the container.

You can now use all the installed tools:

Navigate to your project:

    cd /workspace
    ls # You should see your project files here

Compile and Simulate with GHDL:

    # Analyze/compile VHDL files
    ghdl -a my_design.vhd my_testbench.vhd
    # Elaborate the testbench
    ghdl -e my_testbench
    # Run the simulation and create a VCD waveform file
    ghdl -r my_testbench --vcd=my_testbench.vcd

Simulate with Questa:

GHDL + GTKWave remain the fast iteration loop; Questa is the vendor-accurate simulator with Intel's precompiled device libraries.

    # Compile and run a VHDL testbench in batch mode
    vlib work
    vcom my_design.vhd my_testbench.vhd
    vsim -c my_testbench -do "run -all; quit"

    # Or with the waveform GUI
    vsim my_testbench

To have Quartus hand off to Questa automatically (Tools → Run Simulation Tool / NativeLink), add to your .qsf:

    set_global_assignment -name EDA_SIMULATION_TOOL "Questa Intel FPGA (VHDL)"

Synthesize and Compile with Quartus:

(Assuming you have a Quartus project file, e.g., my_quartus_project.qpf)

    # Run the entire Quartus compilation flow from the command line
    quartus_sh --flow compile my_quartus_project

This will generate your programming file (.sof) in the output_files directory.

Convert Programming File:

openFPGALoader often works best with the Raw Binary File (.rbf) format. Use the Quartus conversion tool to create it from the .sof file.

    quartus_cpf -c output_files/my_quartus_project.sof output_files/my_quartus_project.rbf

Program your FPGA with openFPGALoader:

First, detect the board to make sure it's connected and recognized:

    openFPGALoader --detect

Then, upload your generated bitstream file:

    openFPGALoader -b cyclone10lp your_firmware.rbf

(You may need to adjust the –board or other flags depending on your specific Cyclone 10 LP board.)

Image tags

altera_docker.sh always launches quartus-c10lp:latest. Each time Dockerfile.extras adds something, the image it replaces is kept under a pre-<change> tag so a regression can be bisected without a 35 GB rebuild.

Tag What it is
latest current — carries the Arrow driver, the Nios II library fixes and the RISC-V toolchain
pre-niosv before the RISC-V toolchain was added
arrow the Arrow driver layer, same image pre-niosv points at
pre-arrow before the Arrow USB Programmer library
nios-fix, pre-niosfix around the libfl.so.2 / ncurses fix that unblocked Nios II BSP builds

Rebuild and promote with:

docker build -f Dockerfile.extras -t quartus-c10lp:<change> .
docker tag quartus-c10lp:latest quartus-c10lp:pre-<change>
docker tag quartus-c10lp:<change> quartus-c10lp:latest

Dependencies that no installer provides

Three things this environment needs that are not part of any Intel download. Each one, when absent, fails in a way that points somewhere else entirely.

Arrow USB Programmer hardware library

Quartus ships no plugin for the FT2232H blaster on Trenz boards. Without it jtagconfig reports "No JTAG hardware available" on a board that is powered, connected and enumerating correctly, and nios2-terminal, nios2-download and quartus_pgm are all unavailable. Version 2.5.1 (linux64) from the Trenz download area, baked in by Dockerfile.extras.

It also needs a HOST udev rule releasing ftdi_sio from interface 0 — an image cannot fix that. The rule must match on the interface string, not on the bound driver: at add time nothing is bound yet, so a DRIVERS="ftdi_sio"= match never fires and the rule silently does nothing while looking correct.

RISC-V bare-metal toolchain (Nios V only)

No Intel installer ships one. The Nios V BSP writes riscv32-unknown-elf-gcc into its own generated toolchain.cmake with a plain set(), which shadows any CMake cache override, so the compiler has to exist under exactly that name. niosv-shell looks for Intel's RiscFree under $ACDS_ROOT/riscfree and, not finding it, prints an INFO line and carries on — so the failure surfaces much later as a missing compiler inside a generated CMake build.

xPack riscv-none-elf-gcc 12.5.0-1 is used instead: freely redistributable, no account needed, ships newlib, and rv32ia/ilp32 is an exact multilib with a matching libc.a. The tarball is too large to commit; fetch it next to the Dockerfile before building, and check it against the committed .sha:

V=12.5.0-1
curl -LO https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v$V/xpack-riscv-none-elf-gcc-$V-linux-x64.tar.gz
sha256sum -c xpack-riscv-none-elf-gcc-$V-linux-x64.tar.gz.sha

GCC 12 matches the major version of the Nios II EDS compiler this BSP generation was written alongside. Installed to /opt/riscv-none-elf and symlinked to the riscv32-unknown-elf- prefix by Dockerfile.extras.

Note this does NOT fix the zicsr problem: binutils 2.38 moved the CSR instructions out of the base RISC-V I extension, so the BSP's own crt0.S needs -march=rv32ia_zicsr. That is a BSP setting the consuming project declares — see mk/README.md.

Container library additions

The base image lacks libfl.so.2, which aborts nios2-elf-ar and so every Nios II BSP build, and the ncurses 5 ABI pair nios2-elf-gdb needs. Both come from Dockerfile.extras.

Licensed IP

Quartus Lite needs no license to run, but it will not emit a programming file for a design containing licensed IP. Two cases met on this setup, and they fail differently:

IP FlexLM feature Without a license
Triple-Speed Ethernet altera_eth_tse builds a time-limited .sof that halts after ~1 hour and cannot be converted to .pof, .jic or .rbf
Nios V/m 6AF7_D036 no programming file at all — no evaluation mode exists for it

The Nios V/m failure is the dangerous one. The design synthesises and fits, quartus_asm reports "successful, 0 errors", and output_files/ simply has no .sof in it. The diagnosis is Warning (115003) naming the unlicensed IP and Warning (12192) saying it does not support IP Evaluation Mode. Check for the artefact, not the exit status.

Presence in the Lite IP catalogue and a SUPPORTED_DEVICE_FAMILIES entry naming your device say nothing about licensing. The only probe that answers the question is running the assembler and looking for a programming file.

Using more than one license file

LM_LICENSE_FILE takes a colon-separated list, and altera_docker.sh passes LICENSE_FILE straight through, so several node-locked files coexist:

LICENSE_FILE="/licenses/LR-182581_License.dat:/licenses/questa_license.dat"

Node-locked IP licenses come from the same Self-Service Licensing Center as the Questa one and are tied to the same invented MAC, so no second host ID is needed.

Step 6: Exiting the Container

When you are finished, simply type exit in the container's terminal. Because you used the –rm flag, the container will be automatically removed. Your project files in the mounted directory will remain untouched on your host machine.