21 |
22 | ```
23 | -b, --board BOARD Specify the target board. [required]
24 | -t, --top-module name Set the top-level module name.
25 | -p, --project-dir path Specify the project root directory.
26 | -h, --help Show help message and exit.
27 | ```
28 |
29 |
30 |
--------------------------------------------------------------------------------
/apio/__init__.py:
--------------------------------------------------------------------------------
1 | """Open source ecosystem for open FPGA boards"""
2 |
3 | # -*- coding: utf-8 -*-
4 | # -- This file is part of the Apio project
5 | # -- (C) 2016-2019 FPGAwars
6 | # -- Author Jesús Arroyo
7 | # -- License GPLv2
8 |
9 | # --------------------------------------------
10 | # - Information for the Distribution package
11 | # --------------------------------------------
12 |
13 | # -- Developer: Change this number when releasing a new version
14 | VERSION = (1, 1, 0)
15 |
16 | # -- Get the version as a string. Ex: "0.10.1"
17 | __version__ = ".".join([str(s) for s in VERSION])
18 |
19 | __title__ = "apio"
20 | __description__ = "Open source ecosystem for open FPGA boards"
21 | __url__ = "https://github.com/FPGAwars/apio"
22 |
23 | __author__ = "Jesús Arroyo Torrens"
24 | __email__ = "jesus.arroyo.torrens@gmail.com"
25 |
26 | __license__ = "GPLv2"
27 |
--------------------------------------------------------------------------------
/tests/unit_tests/utils/test_jsonc.py:
--------------------------------------------------------------------------------
1 | """
2 | Tests of jsonc.py
3 | """
4 |
5 | from apio.utils import jsonc
6 |
7 | # -- The converstion input and expected output strings. Notice the '//' within
8 | # -- the string, it should not be classified as a comment. The '_' characters
9 | # -- are place holders for trailing white space.
10 | BEFORE = """
11 | // Comment 1.
12 | "image": {__
13 | // Comment 2.
14 | "src": "Images//Sun.png", // Comment 3
15 | "name": "aaa\n",
16 | "hOffset": 250
17 | }
18 | """
19 |
20 | AFTER = """
21 | ____
22 | "image": {__
23 | ________
24 | "src": "Images//Sun.png",_
25 | "name": "aaa\n",
26 | "hOffset": 250
27 | }
28 | """
29 |
30 |
31 | def test_to_json():
32 | """Test the comments removal."""
33 | assert jsonc.to_json(BEFORE.replace("_", " ")) == AFTER.replace("_", " ")
34 |
--------------------------------------------------------------------------------
/.github/workflows/resources/darwin/distribution.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Apio
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | apio-component.pkg
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.github/workflows/resources/darwin/README.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 | Apio is installed and is ready for use.
12 |
13 | To test it, open a new shell window and try a few
14 | of the commands below. For more information see
15 | https://fpgawars.github.io/apio/quick-start
16 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/docs/cmd-apio-fpgas.md:
--------------------------------------------------------------------------------
1 | # Apio fpgas
2 |
3 | ---
4 |
5 | ## apio fpgas
6 |
7 | The `apio fpgas` command lists FPGAs supported by Apio.
8 |
9 | You can define custom FPGAs by placing a `fpgas.jsonc` file in the project directory,
10 | which overrides the default configuration, provided they are supported by the
11 | underlying tools.
12 |
13 | > The apio board definitions are included in the apio 'definition' package
14 | > that is updated periodically.
15 |
16 |
Examples
17 |
18 | ```
19 | apio fpgas # List all FPGAs.
20 | apio fpgas -v # List with extra columns.
21 | apio fpgas | grep gowin # Filter FPGA results.
22 | apio boards --docs # Generate a report for Apio docs
23 | ```
24 |
25 |
Options
26 |
27 | ```
28 | -v, --verbose Show detailed output
29 | -d, --docs Format for Apio Docs.
30 | -p, --project-dir path Specify the project root directory
31 | -h, --help Show help message and exit
32 | ```
33 |
--------------------------------------------------------------------------------
/tests/unit_tests/managers/test_scons_filters.py:
--------------------------------------------------------------------------------
1 | """
2 | Tests of scons_filters.py
3 | """
4 |
5 | from apio.managers.scons_filter import PnrRangeDetector, PipeId
6 |
7 | # TODO: Add more tests.
8 |
9 |
10 | def test_pnr_range_detector():
11 | """Tests the pnr range class."""
12 |
13 | # -- Create a PNR range detector.
14 | rd = PnrRangeDetector()
15 |
16 | # -- Starting out of range
17 | assert not rd.update(PipeId.STDOUT, "hello world")
18 | assert not rd.update(PipeId.STDOUT, "hello world")
19 |
20 | # -- Start of range trigger (from next line)
21 | assert not rd.update(PipeId.STDOUT, "nextpnr-ice40 bla bla")
22 |
23 | # -- In range.
24 | assert rd.update(PipeId.STDOUT, "bla bla")
25 | assert rd.update(PipeId.STDOUT, "info: bla bla")
26 |
27 | # -- End of range trigger. (from next line)
28 | assert rd.update(PipeId.STDERR, "Program finished normally.")
29 |
30 | # -- out of range.
31 | assert not rd.update(PipeId.STDOUT, "bla bla")
32 |
--------------------------------------------------------------------------------
/docs/cmd-apio-boards.md:
--------------------------------------------------------------------------------
1 | # Apio boards
2 |
3 | ---
4 |
5 | ## apio boards
6 |
7 | The `apio boards` command lists the FPGA boards supported by Apio.
8 |
9 | You can define custom boards by placing a `boards.jsonc` file with your
10 | board definition in your project directory, which overrides Apio’s default `boards.jsonc`.
11 |
12 | > The apio board definitions are included in the apio 'definition' package
13 | > that is updated periodically.
14 |
15 |
Examples
16 |
17 | ```
18 | apio boards # List all boards.
19 | apio boards -v # List with extra columns.
20 | apio boards | grep ecp5 # Filter boards results.
21 | apio boards --docs # Generate a report for Apio docs
22 | ```
23 |
24 |
Options
25 |
26 | ```
27 | -v, --verbose Show detailed output.
28 | -d, --docs Format for Apio Docs.
29 | -p, --project-dir path Set the root directory for the project.
30 | -h, --help Show this message and exit.
31 | ```
32 |
--------------------------------------------------------------------------------
/scripts/browser-test/test.py:
--------------------------------------------------------------------------------
1 | import webbrowser
2 | from pathlib import Path
3 |
4 |
5 | def open_in_browser(file_path):
6 | # Convert file path to a proper file:// URI
7 | file_uri = Path(file_path).resolve().as_uri()
8 |
9 | # Try Chrome first (Windows, macOS, Linux), then fall back
10 | # to default browser
11 |
12 | # browser_options = [
13 | # "chrome", # Windows
14 | # "open -a 'Google Chrome'",# macOS
15 | # "google-chrome", # Linux
16 | # "chromium-browser" # Linux alternative
17 | # ]
18 |
19 | # for option in browser_options:
20 | # try:
21 | # webbrowser.get(option).open(file_uri)
22 | # return
23 | # except webbrowser.Error:
24 | # continue
25 |
26 | # Fallback — use system default browser
27 | webbrowser.open(file_uri)
28 |
29 |
30 | if __name__ == "__main__":
31 | open_in_browser("test.svg")
32 | # open_in_browser("test.pdf")
33 | # open_in_browser("test.png")
34 |
--------------------------------------------------------------------------------
/docs/cmd-apio-lint.md:
--------------------------------------------------------------------------------
1 | # Apio lint
2 |
3 | ---
4 |
5 | ## apio lint
6 |
7 | The `apio lint` command checks the project's source files for errors, inconsistencies, and style violations using the `Verilator` tool included with Apio.
8 |
9 |
Examples
10 |
11 | ```
12 | apio lint # Lint the entire design
13 | apio lint -t my_module # Lint only 'my_module' and its dependencies
14 | apio lint --all # Enable all warnings, including style warnings
15 | ```
16 |
17 |
Options
18 |
19 | ```
20 | --nostyle Disable all style warnings
21 | --nowarn nowarn Disable specific warning(s)
22 | --warn warn Enable specific warning(s)
23 | -a, --all Enable all warnings, including code style warnings
24 | -t, --top-module name Restrict linting to this module and its dependencies
25 | -e, --env name Use a named environment from apio.ini
26 | -p, --project-dir path Specify the project root directory
27 | -h, --help Show help message and exit
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/creating-apio-version.md:
--------------------------------------------------------------------------------
1 | # Creating an Apio version
2 |
3 | !!! warning "Page under construction"
4 | The details of the release process is not finalized yet.
5 |
6 | ## Cutting a release candidate
7 |
8 | TBD
9 |
10 | ## Testing a release candidate
11 |
12 | TBD
13 |
14 | ## Releasing a release candidate as an official release
15 |
16 | TBD
17 |
18 |
19 |
20 | Things to pay attention to:
21 |
22 | - Determine the increment level, patch, minor, or major.
23 | - Setting the new apio version in `apio/__init__`.
24 | - Creating a `.jsonc` remote config file at https://github.com/FPGAwars/apio/tree/develop/remote-config with the desired package versions.
25 | - Waiting for the next daily apio build and make sure the build is green (use its apio commit as the cutoff commit)
26 | - Making sure that the apio test workflows are green for the cutoff commit.
27 | - Creating a new release in the apio repo and adding to it the build files from the build repo.
28 | - Somewhere along these steps, test the release candidate.
29 |
30 |
31 |
--------------------------------------------------------------------------------
/scripts/check-icestudio-test-examples.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Exit on first error.
4 | set -e
5 |
6 | # Change to repo's dir.
7 | cd ..
8 |
9 | # Find all apio.ini in icestudio projects
10 | projects=$(find test-examples | grep icestudio | grep apio.ini)
11 |
12 | for proj in $projects; do
13 | # Get the parent directory of apio.ini
14 | proj="$(dirname "$proj")"
15 |
16 | echo
17 | echo "--- PROJECT $proj"
18 | echo
19 |
20 | # -- Go to the project's dir.
21 | pushd $proj
22 |
23 | # -- Test if the project has testbenches.
24 | set +e
25 | ls *_tb.v
26 | TB_STATUS=$?
27 | set -e
28 |
29 |
30 | # -- Exceute apio commands in the project. They should succeeed.
31 | set -x
32 | apio clean
33 | apio build
34 | apio lint
35 | if [ $TB_STATUS -eq 0 ]; then
36 | apio test
37 | fi
38 | apio graph
39 | apio report
40 | apio clean
41 | set +x
42 |
43 | # -- Go back to the repo's root.
44 | popd
45 |
46 | done
47 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/cmd-apio-preferences.md:
--------------------------------------------------------------------------------
1 | # Apio preferences
2 |
3 | ---
4 |
5 | ## apio preferences
6 |
7 | The `apio preferences` command lets you view and manage user preferences. These settings are stored in the `profile.json` file in the Apio home directory (e.g. `~/.apio`) and apply to all Apio projects.
8 |
9 | > To review the available themes on your screen type `apio info themes`.
10 |
11 |
Examples
12 |
13 | ```
14 | apio preferences -t light # Set theme for light backgrounds
15 | apio preferences -t dark # Set theme for dark backgrounds
16 | apio preferences -t no-colors # Disable color output
17 | apio preferences --list # Show current preferences
18 | apio pref -t dark # Using command shortcut
19 | ```
20 |
21 |
Options
22 |
23 | ```
24 | -t, --theme [light|dark|no-colors] Set color theme
25 | -c, --colors List available theme colors
26 | -l, --list Show current preferences
27 | -h, --help Show help message and exit
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/board-drivers.md:
--------------------------------------------------------------------------------
1 | # FPGA Board Drivers
2 |
3 | Some FPGA boards require a system driver before the programmer can access them. When this is the case, install the driver with the `apio drivers install` command.
4 |
5 | Apio provides two types of driver: `ftdi` and `serial`. The table below compares them.
6 |
7 | > - Driver installation is not required on macOS.
8 |
9 | > - If the FPGA board appears in the device list with `--unavail--` manufacturer or product strings, the appropriate driver probably needs to be installed.
10 |
11 | | | FTDI driver | Serial driver |
12 | | ---------------- | :---------------------------- | :------------------------------ |
13 | | Platforms | Linux, Windows | Linux, Windows |
14 | | Driver install | `apio drivers install ftdi` | `apio drivers install serial` |
15 | | Driver uninstall | `apio drivers uninstall ftdi` | `apio drivers uninstall serial` |
16 | | List devices | `apio devices usb` | `apio devices serial` |
17 |
--------------------------------------------------------------------------------
/docs/contributing-definitions.md:
--------------------------------------------------------------------------------
1 | # Contributing boards definitions
2 |
3 | Once you tested your custom boards, FPGAs, and programmers definitions
4 | as describe in the [Custom Boards](custom-boards.md) page, you can contribute
5 | them to the Apio project so they can benefit other users as well.
6 |
7 | > When contributing a new board definition, it's is highly recommended to
8 | > contribute also and example for that board. Please wait for the new board
9 | > definitions to be released before contributing examples for that board.
10 |
11 | Apio definitions are stored in the Apio `definitions` repository at [https://github.com/FPGAwars/apio-definitions ](https://github.com/FPGAwars/apio-definitions). Simply submit a pull request with the modified files.
12 |
13 | > Note that your contribution will be available to users only after Apio
14 | > will be configured remotely to use the new version of the `definitions`
15 | > package. The Apio remote configuration are served from [https://github.com/FPGAwars/apio/tree/develop/remote-config](https://github.com/FPGAwars/apio/tree/develop/remote-config)
16 |
--------------------------------------------------------------------------------
/tests/unit_tests/common/test_apio_console.py:
--------------------------------------------------------------------------------
1 | """Test for the apio_console.py."""
2 |
3 | from apio.common import apio_console
4 | from apio.common.apio_console import (
5 | FORCE_TERMINAL,
6 | cstyle,
7 | cunstyle,
8 | )
9 |
10 |
11 | def test_style_unstyle():
12 | """Test the styling and unstyling functions"""
13 |
14 | apio_console.configure(terminal_mode=FORCE_TERMINAL, theme_name="light")
15 |
16 | # -- Test cstyle()
17 | assert cstyle("") == ""
18 | assert cstyle("", style="red") == ""
19 | assert cstyle("abc xyz", style="red") == "\x1b[31mabc xyz\x1b[0m"
20 | assert cstyle("abc xyz", style="cyan bold") == "\x1b[1;36mabc xyz\x1b[0m"
21 | assert cstyle("ab \n xy", style="cyan bold") == "\x1b[1;36mab \n xy\x1b[0m"
22 |
23 | # -- Test cunstyle() with plain text.
24 | assert cunstyle("") == ""
25 | assert cunstyle("abc xyz") == "abc xyz"
26 |
27 | # -- Test cunstyle() with colored text.
28 | assert cunstyle(cstyle("")) == ""
29 | assert cunstyle(cstyle("abc xyz")) == "abc xyz"
30 | assert cunstyle(cstyle("ab \n xy")) == "ab \n xy"
31 |
--------------------------------------------------------------------------------
/.github/workflows/publish-to-pypi.yaml:
--------------------------------------------------------------------------------
1 | name: publish-to-pypi
2 |
3 | # Manual activation
4 | on: [workflow_dispatch]
5 |
6 | jobs:
7 | # -- Publish a new Apio release
8 | publish:
9 | runs-on: ubuntu-22.04
10 | steps:
11 | # -- Checkout the main branch
12 | - name: Checkout sources
13 | uses: actions/checkout@v4
14 | with:
15 | ref: master
16 |
17 | # -- Install and and configure python
18 | - name: Set up Python
19 | uses: actions/setup-python@v5
20 | with:
21 | python-version: "3.13"
22 |
23 | # -- Install all the dependencies needed
24 | # - name: Install dependencies
25 | # run: |
26 | # make deps
27 |
28 | # -- Publish to Pypi!!
29 | - name: Publish to PyPi
30 | env:
31 | FLIT_USERNAME: ${{ secrets.PYPI_USERNAME }}
32 | FLIT_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
33 | run: |
34 | invoke publish
35 |
--------------------------------------------------------------------------------
/apio/common/proto/update-protos.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Run this script each time apio.proto is modified.
4 |
5 | # Input:
6 | # apio.proto - proto messages definitions.
7 | #
8 | # Outputs:
9 | # apio_pb2.py - python binding.
10 | # apio_pb2.pyi - symbols for visual studio code.
11 |
12 |
13 |
14 | # Exit on any error.
15 | set -e
16 |
17 | # This is the proto compiler
18 | echo "Installing the proto compiler"
19 | pip install --quiet grpcio-tools==1.76.0
20 |
21 | patch="
22 | # pylint: disable=all
23 | "
24 |
25 | tmp_file="_tmp"
26 |
27 | patch_proto () {
28 | f=$1
29 | echo "Patching $f"
30 | mv $1 $tmp_file
31 | echo "$patch" > $1
32 | cat $tmp_file >> $1
33 | rm $tmp_file
34 | }
35 |
36 | # Clean old output files.
37 | rm -f apio_pb2.py
38 | rm -f apio_pb2.pyi
39 | rm -f $tmp_file
40 |
41 | # Generate new
42 | echo "Compiling apio.proto"
43 | python -m grpc_tools.protoc \
44 | -I. \
45 | --python_out=. \
46 | --pyi_out=. apio.proto
47 |
48 | # Inject the pylint directive to supress warnings.
49 | patch_proto apio_pb2.py
50 | patch_proto apio_pb2.pyi
51 |
52 | # All done OK
53 | echo "All done"
54 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_sim.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio sim" command"""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_sim(apio_runner: ApioRunner):
8 | """Test: apio sim
9 | when no apio.ini file is given
10 | No additional parameters are given
11 | """
12 |
13 | with apio_runner.in_sandbox() as sb:
14 |
15 | # -- apio sim
16 | result = sb.invoke_apio_cmd(apio, ["sim"])
17 | assert result.exit_code != 0, result.output
18 | # -- TODO
19 |
20 |
21 | def test_sim_with_env_arg_error(apio_runner: ApioRunner):
22 | """Tests the command with an invalid --env value. This error message
23 | confirms that the --env arg was propagated to the apio.ini loading
24 | logic."""
25 |
26 | with apio_runner.in_sandbox() as sb:
27 |
28 | # -- Run "apio sim --env no-such-env"
29 | sb.write_apio_ini({"[env:default]": {"top-module": "main"}})
30 | result = sb.invoke_apio_cmd(apio, ["sim", "--env", "no-such-env"])
31 | assert result.exit_code == 1, result.output
32 | assert (
33 | "Error: Env 'no-such-env' not found in apio.ini" in result.output
34 | )
35 |
--------------------------------------------------------------------------------
/docs/cmd-apio-test.md:
--------------------------------------------------------------------------------
1 | # Apio test
2 |
3 | ---
4 |
5 | ## apio test
6 |
7 | The `apio test` command simulates one or more testbenches in the project.
8 | It is intended for automated testing of your design. Testbenches should
9 | have filenames ending in `_tb` (e.g., `my_module_tb.v`) and should use
10 | the `$fatal` directive to indicate errors.
11 |
12 |
Examples
13 |
14 | ```
15 | apio test # Run all *_tb.v testbenches
16 | apio test my_module_tb.v # Run a single testbench
17 | ```
18 |
19 |
Options
20 |
21 | ```
22 | -e, --env name Use a named environment from apio.ini
23 | -p, --project-dir path Specify the project root directory
24 | -h, --help Show help message and exit
25 | ```
26 |
27 |
Notes
28 |
29 | - Avoid using the Verilog `$dumpfile()` function, as it may override
30 | the default name and location Apio assigns for the generated `.vcd` file.
31 |
32 | - Testbench paths must be relative to the project directory,
33 | even when using the `--project-dir` option.
34 |
35 | - See the Apio example `alhambra-ii/getting-started` for a testbench
36 | that demonstrates recommended practices.
37 |
38 | - For graphical signal visualization, use the `apio sim` command instead.
39 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_graph.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio graph" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_graph_no_apio_ini(apio_runner: ApioRunner):
8 | """Test: apio graph with no apio.ini"""
9 |
10 | with apio_runner.in_sandbox() as sb:
11 |
12 | # -- Execute "apio graph"
13 | result = sb.invoke_apio_cmd(apio, ["graph"])
14 | assert result.exit_code == 1, result.output
15 | assert "Error: Missing project file apio.ini" in result.output
16 |
17 |
18 | def test_graph_with_env_arg_error(apio_runner: ApioRunner):
19 | """Tests the command with an invalid --env value. This error message
20 | confirms that the --env arg was propagated to the apio.ini loading
21 | logic."""
22 |
23 | with apio_runner.in_sandbox() as sb:
24 |
25 | # -- Run "apio graph --env no-such-env"
26 | sb.write_apio_ini({"[env:default]": {"top-module": "main"}})
27 | result = sb.invoke_apio_cmd(apio, ["graph", "--env", "no-such-env"])
28 | assert result.exit_code == 1, result.output
29 | assert (
30 | "Error: Env 'no-such-env' not found in apio.ini" in result.output
31 | )
32 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_lint.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio lint" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_lint_apio_init(apio_runner: ApioRunner):
8 | """Test: apio lint without an apio.ini project file."""
9 |
10 | with apio_runner.in_sandbox() as sb:
11 |
12 | # -- Execute "apio lint"
13 | result = sb.invoke_apio_cmd(apio, ["lint"])
14 | assert result.exit_code == 1, result.output
15 | assert "Error: Missing project file apio.ini" in result.output
16 |
17 |
18 | def test_lint_with_env_arg_error(apio_runner: ApioRunner):
19 | """Tests the command with an invalid --env value. This error message
20 | confirms that the --env arg was propagated to the apio.ini loading
21 | logic."""
22 |
23 | with apio_runner.in_sandbox() as sb:
24 |
25 | # -- Run "apio lint --env no-such-env"
26 | sb.write_apio_ini({"[env:default]": {"top-module": "main"}})
27 | result = sb.invoke_apio_cmd(apio, ["lint", "--env", "no-such-env"])
28 | assert result.exit_code == 1, result.output
29 | assert (
30 | "Error: Env 'no-such-env' not found in apio.ini" in result.output
31 | )
32 |
--------------------------------------------------------------------------------
/docs/cmd-apio-upload.md:
--------------------------------------------------------------------------------
1 | # Apio upload
2 |
3 | ---
4 |
5 | ## apio upload
6 |
7 | The `apio upload` command builds the bitstream (like `apio build`) and uploads it to the FPGA board.
8 |
9 |
Examples
10 |
11 | ```
12 | apio upload # Typical usage
13 | apio upload -s /dev/cu.usbserial-1300 # Specify serial port
14 | apio upload -n FTXYA34Z # Specify USB serial number
15 | ```
16 |
17 |
Options
18 |
19 | ```
20 | -s, --serial-port serial-port Specify the serial port
21 | -n, --serial-num serial-num Specify the device's USB serial number
22 | -e, --env name Use a named environment from apio.ini
23 | -p, --project-dir path Specify the project root directory
24 | -h, --help Show this help message and exit
25 | ```
26 |
27 |
Notes
28 |
29 | - In most cases, `apio upload` is enough to locate and program the FPGA board. Use the `--serial-port` or `--serial-num` options to select a specific board if multiple matching devices are connected.
30 |
31 | - Use `apio devices` to list connected USB and serial devices, and `apio drivers` to install or uninstall device drivers.
32 |
33 | - You can override the board's default programmer using the `programmer-cmd` option in `apio.ini`.
34 |
--------------------------------------------------------------------------------
/docs/cmd-apio-build.md:
--------------------------------------------------------------------------------
1 | # Apio build
2 |
3 | ---
4 |
5 | ## apio build
6 |
7 | The `apio build` command compiles the project's source files and
8 | generates a bitstream ready for upload to the FPGA.
9 |
10 |
Examples
11 |
12 | ```
13 | apio build # Typical Usage
14 | apio build -e debug # Use a specific environment from apio.ini
15 | apio build -v # Show all verbose output
16 | apio build --verbose-synth # Verbose synthesis info
17 | apio build --verbose-pnr # Verbose place and route info
18 | ```
19 |
20 |
Options
21 |
22 | ```
23 | -e, --env name Use a named environment from apio.ini
24 | -p, --project-dir path Set the project's root directory
25 | -v, --verbose Show all verbose output
26 | --verbose-synth Show verbose synthesis stage output
27 | --verbose-pnr Show verbose place-and-route stage output
28 | -h, --help Show help message and exit
29 | ```
30 |
31 |
Notes
32 |
33 | - Specify the top module using the `top-module` option in `apio.ini`.
34 | - Testbench files (`*_tb.v` and `*_tb.sv`) are ignored during build.
35 | - Running `apio build` before `apio upload` is usually unnecessary.
36 | - Run `apio clean` before building to force a full rebuild.
37 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_report.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio report" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_report_no_apio(apio_runner: ApioRunner):
8 | """Tests the apio report command without an apio.ini file."""
9 |
10 | with apio_runner.in_sandbox() as sb:
11 |
12 | # -- Run "apio report" without apio.ini
13 | result = sb.invoke_apio_cmd(apio, ["report"])
14 | assert result.exit_code != 0, result.output
15 | assert "Error: Missing project file apio.ini" in result.output
16 |
17 |
18 | def test_report_with_env_arg_error(apio_runner: ApioRunner):
19 | """Tests the command with an invalid --env value. This error message
20 | confirms that the --env arg was propagated to the apio.ini loading
21 | logic."""
22 |
23 | with apio_runner.in_sandbox() as sb:
24 |
25 | # -- Run "apio report --env no-such-env"
26 | sb.write_apio_ini({"[env:default]": {"top-module": "main"}})
27 | result = sb.invoke_apio_cmd(apio, ["report", "--env", "no-such-env"])
28 | assert result.exit_code == 1, result.output
29 | assert (
30 | "Error: Env 'no-such-env' not found in apio.ini" in result.output
31 | )
32 |
--------------------------------------------------------------------------------
/docs/cmd-apio-raw.md:
--------------------------------------------------------------------------------
1 | # Apio raw
2 |
3 | ---
4 |
5 | ## apio raw
6 |
7 | The `apio raw` command bypasses Apio's usual workflow to run tools directly. It is intended for advanced users familiar with those tools.
8 |
9 | > Before execution, Apio temporarily modifies environment variables like `$PATH`
10 | > to make its packages accessible. Use the option `--verbose` to view these changes.
11 |
12 |
Examples
13 |
14 | ```
15 | apio raw -- yosys --version # Show Yosys version
16 | apio raw -v -- yosys --version # Verbose output
17 | apio raw -- yosys # Start Yosys in interactive mode
18 | apio raw -- icepll -i 12 -o 30 # Calculate ICE PLL parameters
19 | apio raw -- which yosys # Locate yosys in the path
20 | apio raw -- bash # Open a shell with Apio's env.
21 | apio raw -- zadig # Run Zadig (on Windows).
22 | apio raw -v # Show Apio environment settings
23 | apio raw -h # Show help message
24 | ```
25 |
26 | > If a command is specified, it must be prefixed with the `--` marker to
27 | > distinguish it from the apio command args.
28 |
29 |
Options
30 |
31 | ```
32 | -v, --verbose Show detailed output
33 | -h, --help Show help message and exit
34 | ```
35 |
--------------------------------------------------------------------------------
/apio/commands/apio_drivers.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # -- This file is part of the Apio project
3 | # -- (C) 2016-2024 FPGAwars
4 | # -- Authors
5 | # -- * Jesús Arroyo (2016-2019)
6 | # -- * Juan Gonzalez (obijuan) (2019-2024)
7 | # -- License GPLv2
8 | """Implementation of 'apio drivers' command group."""
9 |
10 | import click
11 | from apio.utils.cmd_util import ApioGroup, ApioSubgroup
12 | from apio.commands import (
13 | apio_drivers_install,
14 | apio_drivers_uninstall,
15 | )
16 |
17 |
18 | # --- apio drivers
19 |
20 | # -- Text in the rich-text format of the python rich library.
21 | APIO_DRIVERS_HELP = """
22 | The command group 'apio drivers' contains subcommands to manage the \
23 | drivers on your system.
24 | """
25 |
26 | # -- We have only a single group with the title 'Subcommands'.
27 | SUBGROUPS = [
28 | ApioSubgroup(
29 | "Subcommands",
30 | [
31 | apio_drivers_install.cli,
32 | apio_drivers_uninstall.cli,
33 | ],
34 | )
35 | ]
36 |
37 |
38 | @click.command(
39 | name="drivers",
40 | cls=ApioGroup,
41 | subgroups=SUBGROUPS,
42 | short_help="Manage the operating system drivers.",
43 | help=APIO_DRIVERS_HELP,
44 | )
45 | def cli():
46 | """Implements the apio drivers command."""
47 |
48 | # pass
49 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_format.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio format" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_format_without_apio_ini(apio_runner: ApioRunner):
8 | """Tests the apio format command with a missing apio.ini file."""
9 |
10 | with apio_runner.in_sandbox() as sb:
11 |
12 | # -- Run "apio format" with no apio.ini
13 | result = sb.invoke_apio_cmd(apio, ["format"])
14 | assert result.exit_code != 0, result.output
15 | assert "Error: Missing project file apio.ini" in result.output
16 |
17 |
18 | def test_format_with_env_arg_error(apio_runner: ApioRunner):
19 | """Tests the command with an invalid --env value. This error message
20 | confirms that the --env arg was propagated to the apio.ini loading
21 | logic."""
22 |
23 | with apio_runner.in_sandbox() as sb:
24 |
25 | # -- Run "apio format --env no-such-env"
26 | sb.write_apio_ini({"[env:default]": {"top-module": "main"}})
27 | result = sb.invoke_apio_cmd(apio, ["format", "--env", "no-such-env"])
28 | assert result.exit_code == 1, result.output
29 | assert (
30 | "Error: Env 'no-such-env' not found in apio.ini" in result.output
31 | )
32 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_test.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio test" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_test(apio_runner: ApioRunner):
8 | """Test: apio test
9 | when no apio.ini file is given
10 | No additional parameters are given
11 | """
12 |
13 | with apio_runner.in_sandbox() as sb:
14 |
15 | # -- Execute "apio test"
16 | result = sb.invoke_apio_cmd(apio, ["test"])
17 | assert result.exit_code != 0, result.output
18 | assert "Error: Missing project file apio.ini" in result.output
19 |
20 |
21 | def test_with_env_arg_error(apio_runner: ApioRunner):
22 | """Tests the command with an invalid --env value. This error message
23 | confirms that the --env arg was propagated to the apio.ini loading
24 | logic."""
25 |
26 | with apio_runner.in_sandbox() as sb:
27 |
28 | # -- Run "apio test --env no-such-env"
29 | sb.write_apio_ini({"[env:default]": {"top-module": "main"}})
30 | result = sb.invoke_apio_cmd(apio, ["test", "--env", "no-such-env"])
31 | assert result.exit_code == 1, result.output
32 | assert (
33 | "Error: Env 'no-such-env' not found in apio.ini" in result.output
34 | )
35 |
--------------------------------------------------------------------------------
/apio/resources/80-fpga-ftdi.rules:
--------------------------------------------------------------------------------
1 | ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0660", GROUP="plugdev", TAG+="uaccess"
2 | ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", MODE="0660", GROUP="plugdev", TAG+="uaccess"
3 | ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE="0660", GROUP="plugdev", TAG+="uaccess"
4 | ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5af0", MODE="0660", GROUP="plugdev", TAG+="uaccess"
5 | ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5bf0", MODE="0660", GROUP="plugdev", TAG+="uaccess"
6 |
7 | # Ulx3s boards
8 | # -- fujprog libusb access
9 | ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE="666", GROUP="dialout"
10 | # -- For usb-serial tty device
11 | SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE="664", GROUP="dialout"
12 |
13 | # -- for iCESugar board
14 | ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="602b", MODE="0660", GROUP="plugdev", TAG+="uaccess"
15 |
16 | # -- for Fomu board
17 | SUBSYSTEM=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5bf0", MODE="0664", GROUP="plugdev"
18 |
19 | # -- for USB-Blaster JTAG Programmer
20 | ATTRS{idVendor}=="09FB", ATTRS{idProduct}=="6001", MODE="0660", GROUP="plugdev", TAG+="uaccess"
21 |
22 | # -- for pico-ice board
23 | ATTRS{idVendor}=="1209", ATTRS{idProduct}=="b1c0", MODE="0660", GROUP="plugdev", TAG+="uaccess"
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio" (root) command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_apio_cmd(apio_runner: ApioRunner):
8 | """Test the apio root command."""
9 |
10 | with apio_runner.in_sandbox() as sb:
11 |
12 | # -- Run 'apio'
13 | result = sb.invoke_apio_cmd(apio, [])
14 | sb.assert_result_ok(result)
15 | assert "WORK WITH FPGAs WITH EASE." in result.output
16 | assert "Build commands:" in result.output
17 | assert "Upload the bitstream to the FPGA" in result.output
18 |
19 | # -- Run 'apio --help'
20 | result = sb.invoke_apio_cmd(apio, ["--help"])
21 | sb.assert_result_ok(result)
22 | assert "WORK WITH FPGAs WITH EASE." in result.output
23 | assert "Build commands:" in result.output
24 | assert "Upload the bitstream to the FPGA" in result.output
25 |
26 | # -- Run 'apio --version'
27 | result = sb.invoke_apio_cmd(apio, ["--version"])
28 | sb.assert_result_ok(result)
29 | assert "apio, version" in result.output
30 |
31 | # -- Run 'apio badcommand'
32 | result = sb.invoke_apio_cmd(apio, ["badcommand"])
33 | assert result.exit_code != 0
34 | assert "Error: No such command 'badcommand'" in result.output
35 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_upload.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio upload" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_upload_without_apio_ini(apio_runner: ApioRunner):
8 | """Test: apio upload
9 | when no apio.ini file is given
10 | No additional parameters are given
11 | """
12 |
13 | with apio_runner.in_sandbox() as sb:
14 |
15 | # -- Execute "apio upload"
16 | result = sb.invoke_apio_cmd(apio, ["upload"])
17 |
18 | # -- Check the result
19 | assert result.exit_code == 1, result.output
20 | assert "Error: Missing project file apio.ini" in result.output
21 |
22 |
23 | def test_upload_with_env_arg_error(apio_runner: ApioRunner):
24 | """Tests the command with an invalid --env value. This error message
25 | confirms that the --env arg was propagated to the apio.ini loading
26 | logic."""
27 |
28 | with apio_runner.in_sandbox() as sb:
29 |
30 | # -- Run "apio upload --env no-such-env"
31 | sb.write_apio_ini({"[env:default]": {"top-module": "main"}})
32 | result = sb.invoke_apio_cmd(apio, ["upload", "--env", "no-such-env"])
33 | assert result.exit_code == 1, result.output
34 | assert (
35 | "Error: Env 'no-such-env' not found in apio.ini" in result.output
36 | )
37 |
--------------------------------------------------------------------------------
/docs/contributing-examples.md:
--------------------------------------------------------------------------------
1 | # Contributing Apio examples
2 |
3 | Apio examples are stored in the `examples` directory of the [FPGAwars/apio-examples](https://github.com/FPGAwars/apio-examples) repository and are distributed in the Apio `examples` package (type `apio examples list` to check its version).
4 |
5 | To contribute a new example, submit a pull request to the [FPGAwars/apio-examples](https://github.com/FPGAwars/apio-examples) repository after confirming it meets the following requirements:
6 |
7 | - The example includes an `info` file with a single-line description (max 50 characters).
8 | - The example passes linting with no warnings and can be built and uploaded.
9 | - The example path is `examples//`, where `` matches the value in `apio.ini` and `` includes only lowercase letters (a–z), digits (0–9), and dashes (-), and begins with a letter.
10 | - The example includes at least one testbench that passes `apio test` and `apio sim` without errors.
11 | - Each testbench has a corresponding `.gtkw` file with a saved GTKWave state.
12 |
13 | ## NOTES
14 |
15 | - Files may be placed in a single directory or organized into subdirectories.
16 | - Verilog `.v` and SystemVerilog `.sv` files may be used in any combination.
17 | - If `apio.ini` defines multiple environments, the rules above apply to each environment selected with the `--env` flag.
18 |
--------------------------------------------------------------------------------
/.github/workflows/resources/windows/innosetup.template:
--------------------------------------------------------------------------------
1 | ; Config file for building Apio's InnoSetup installer.
2 | ; [APIO_VERSION] is a placeholder.
3 |
4 | [Setup]
5 | ChangesEnvironment=yes
6 | AppName=Apio
7 | AppVersion=[APIO_VERSION]
8 | DefaultDirName={commonpf}\Apio
9 | ArchitecturesInstallIn64BitMode=x64
10 | DefaultGroupName=Apio
11 | OutputDir=.
12 | OutputBaseFilename=innosetup-installer
13 |
14 | [Files]
15 | Source: "_dist\apio\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
16 | Source: ".github\workflows\resources\windows\README.txt"; DestDir: "{app}"; Flags: isreadme
17 |
18 | ; Code from here is based on the answers at
19 | ; https://stackoverflow.com/questions/3304463/how-do-i-modify-the-path-environment-variable-when-running-an-inno-setup-install
20 |
21 | [Registry]
22 | Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"
23 |
24 | [CustomMessages]
25 | AppAddPath=Add application directory to your environmental path (required)
26 |
27 | [Tasks]
28 | Name: modifypath; Description:{cm:AppAddPath};
29 |
30 | [Code]
31 | const
32 | ModPathName = 'modifypath';
33 | ModPathType = 'system';
34 |
35 | function ModPathDir(): TArrayOfString;
36 | begin
37 | setArrayLength(Result, 1)
38 | Result[0] := ExpandConstant('{app}');
39 | end;
40 |
41 | #include ".github/workflows/resources/windows/modpath.iss"
42 |
43 |
44 |
--------------------------------------------------------------------------------
/docs/cmd-apio-graph.md:
--------------------------------------------------------------------------------
1 | # Apio graph
2 |
3 | ---
4 |
5 | ## apio graph
6 |
7 | The `apio graph` command generates a graphical representation of the hardware
8 | design and then opens it in a graphical viewer.
9 |
10 |
22 |
23 | ```
24 | --svg Generate an SVG file (default)
25 | --png Generate a PNG file
26 | --pdf Generate a PDF file
27 | -e, --env name Use a named environment from apio.ini
28 | -p, --project-dir path Specify the project root directory
29 | -t, --top-module name Set the top-level module to graph
30 | -n, --no-viewer Do not open graph viewer
31 | -v, --verbose Show detailed output
32 | -h, --help Show help message and exit
33 | ```
34 |
35 |
Notes
36 |
37 | - On Windows, run `explorer _build/default/graph.svg` to view the graph.
38 | If your environment name is different from `default`, adjust the path accordingly.
39 | - On macOS, use `open _build/default/graph.svg`.
40 |
41 |
Example output
42 |
43 | 
44 |
--------------------------------------------------------------------------------
/tests/unit_tests/scons/test_apio_env.py:
--------------------------------------------------------------------------------
1 | """
2 | Tests of the scons ApioEnv.
3 | """
4 |
5 | from tests.unit_tests.scons.testing import make_test_apio_env
6 | from tests.conftest import ApioRunner
7 | from apio.scons.apio_env import ApioEnv
8 |
9 |
10 | def test_env_is_debug(apio_runner: ApioRunner):
11 | """Tests the env handling of the in_debug var."""
12 |
13 | with apio_runner.in_sandbox():
14 |
15 | env: ApioEnv = make_test_apio_env(debug_level=2)
16 | assert env.is_debug(1)
17 | assert env.is_debug(2)
18 | assert not env.is_debug(3)
19 |
20 | env: ApioEnv = make_test_apio_env(debug_level=0)
21 | assert not env.is_debug(1)
22 | assert not env.is_debug(2)
23 | assert not env.is_debug(3)
24 |
25 |
26 | def test_env_platform_id():
27 | """Tests the env handling of the platform_id param."""
28 |
29 | # -- Test with a non windows platform id.
30 | env = make_test_apio_env(platform_id="darwin-arm64", is_windows=False)
31 | assert not env.is_windows
32 |
33 | # -- Test with a windows platform id.
34 | env = make_test_apio_env(platform_id="windows-amd64", is_windows=True)
35 | assert env.is_windows
36 |
37 |
38 | def test_targeting():
39 | """Test the targeting() method."""
40 |
41 | # -- The test env targets 'build'.
42 | apio_env = make_test_apio_env()
43 |
44 | assert apio_env.targeting("build")
45 | assert apio_env.targeting("upload", "build")
46 | assert not apio_env.targeting("upload")
47 |
--------------------------------------------------------------------------------
/docs/cmd-apio-report.md:
--------------------------------------------------------------------------------
1 | # Apio report
2 |
3 | ---
4 |
5 | ## apio report
6 |
7 | The `apio report` command provides resource utilization and timing
8 | information for the design. It helps identify bottlenecks and verify
9 | whether the design meets its target clock speed.
10 |
11 |
19 |
20 | ```
21 | -e, --env name Use a named environment from apio.ini
22 | -p, --project-dir path Specify the project root directory
23 | -v, --verbose Show detailed output
24 | -h, --help Show help message and exit
25 | ```
26 |
27 |
Example report
28 |
29 | ```
30 | FPGA Resource Utilization
31 | ┌────────────────┬────────┬──────────┬─────────┐
32 | │ RESOURCE │ USED │ TOTAL │ UTIL. │
33 | ├────────────────┼────────┼──────────┼─────────┤
34 | │ ICESTORM_LC │ 90 │ 7680 │ 1% │
35 | │ ICESTORM_PLL │ │ 2 │ │
36 | │ ICESTORM_RAM │ │ 32 │ │
37 | │ SB_GB │ 2 │ 8 │ 25% │
38 | │ SB_IO │ 9 │ 256 │ 3% │
39 | │ SB_WARMBOOT │ │ 1 │ │
40 | └────────────────┴────────┴──────────┴─────────┘
41 |
42 | Clock Information
43 | ┌─────────┬───────────────────┐
44 | │ CLOCK │ MAX SPEED [Mhz] │
45 | ├─────────┼───────────────────┤
46 | │ CLK │ 119.15 │
47 | └─────────┴───────────────────┘
48 | ```
49 |
--------------------------------------------------------------------------------
/scripts/diff_jsonc.py:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/python
2 |
3 | """Diff two JSONC files, ignoring comments and key order."""
4 |
5 | # pylint: disable=import-error
6 |
7 | # Requires:
8 | # pip install deepdiff
9 |
10 | import json
11 | import sys
12 | import re
13 | from pathlib import Path
14 | from deepdiff import DeepDiff
15 |
16 |
17 | def remove_jsonc_comments(text):
18 | """Remove // and /* */ comments."""
19 | text = re.sub(r"//.*", "", text)
20 | text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
21 | return text
22 |
23 |
24 | def load_and_normalize_jsonc(path):
25 | """Load the jsonc file as a tree of dics."""
26 | raw = Path(path).read_text(encoding="utf-8")
27 | cleaned = remove_jsonc_comments(raw)
28 | data = json.loads(cleaned)
29 | return data
30 |
31 |
32 | if __name__ == "__main__":
33 | if len(sys.argv) != 3:
34 | print("Usage: python diff_jsonc.py file1.jsonc file2.jsonc")
35 | sys.exit(1)
36 |
37 | file1 = load_and_normalize_jsonc(sys.argv[1])
38 | file2 = load_and_normalize_jsonc(sys.argv[2])
39 |
40 | # print("---File 1 keys begin")
41 | # keys_list = file1.keys()
42 | # keys_list = sorted(keys_list)
43 | # for key in keys_list:
44 | # print(key)
45 | # print("---File 1 keys end")
46 |
47 | diff = DeepDiff(file1, file2, ignore_order=True)
48 | if diff:
49 | print("Differences found:")
50 | print(diff.pretty())
51 | else:
52 | print("Files are equal (ignoring comments and key order).")
53 |
--------------------------------------------------------------------------------
/docs/system-requirements.md:
--------------------------------------------------------------------------------
1 | # Apio System Requirements
2 |
3 | > The information on this page was last updated in October 2025.
4 |
5 | ## Operating System
6 |
7 | Apio is tested on the following platforms (as of Nov 2025):
8 |
9 | | Apio Platform Code | Description | Tested Github Versions | Apio IDE | Apio CLI |
10 | | :----------------- | :-------------------- | :--------------------- | -------- | -------- |
11 | | darwin_arm64 | macOS (Apple Silicon) | macos-latest | YES | YES |
12 | | darwin_x86_64 | macOS (Intel) | macos-15-intel | | YES |
13 | | linux_x86_64 | Linux x86 64-bit | ubuntu-22.04 | YES | YES |
14 | | linux_aarch64 | Linux ARM 64-bit | (not tested) | | YES |
15 | | windows_amd64 | Windows x86 64-bit | windows-latest | YES | YES |
16 |
17 | > The tested version are set in the Apio github test workflow.
18 |
19 | ## Python
20 |
21 | These requirements apply only when installing Apio as a Pip package (Python-based installation).
22 |
23 | > Python is not required when installing Apio using an installer, Debian package, or a file bundle.
24 |
25 | > To test the Python version, run `python --version`. To download Python, visit [python.org](https://www.python.org/downloads/).
26 |
27 | | Python Version | Status |
28 | | :------------- | :---------- |
29 | | 3.14.x | Recommended |
30 | | 3.13.x | Supported |
31 | | 3.12.x | Supported |
32 | | 3.11.x | Supported |
33 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_shortcuts.py:
--------------------------------------------------------------------------------
1 | """Test command shortcuts."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_command_shortcuts(apio_runner: ApioRunner):
8 | """Test the apio root command."""
9 |
10 | with apio_runner.in_sandbox() as sb:
11 |
12 | # -- Run 'apio preferences --list'
13 | # -- Exact match.
14 | result = sb.invoke_apio_cmd(apio, ["preferences", "--list"])
15 | sb.assert_result_ok(result)
16 | assert "Theme" in result.output
17 | assert "light" in result.output
18 |
19 | # -- Run 'apio pr -xyz'
20 | # -- Unique match.
21 | result = sb.invoke_apio_cmd(apio, ["pr", "-xyz"])
22 | assert result.exit_code != 0
23 | assert "Usage: apio preferences" in result.output
24 |
25 | # -- Run 'apio pr -h'
26 | # -- Help text should contain full commands..
27 | result = sb.invoke_apio_cmd(apio, ["pr", "-h"])
28 | sb.assert_result_ok(result)
29 | assert "Usage: apio preferences" in result.output
30 |
31 | # -- Run 'apio p'
32 | # -- Ambiguous match
33 | result = sb.invoke_apio_cmd(apio, ["p", "--list"])
34 | assert result.exit_code != 0
35 | assert "'p' is ambagious: ['packages', 'preferences']" in result.output
36 |
37 | # -- Run 'apio xyz --list'
38 | # -- No match
39 | result = sb.invoke_apio_cmd(apio, ["xyz", "--list"])
40 | assert result.exit_code != 0
41 | assert "No such command 'xyz'" in result.output
42 |
--------------------------------------------------------------------------------
/docs/cmd-apio-format.md:
--------------------------------------------------------------------------------
1 | # Apio format
2 |
3 | ---
4 |
5 | ## apio format
6 |
7 | The `apio format` command formats project source files to ensure consistent
8 | style without changing their behavior. You can format specific files or let
9 | it format all project files by default.
10 |
11 | > File paths are always relative to the project directory, even when using `--project-dir`.
12 |
13 |
Examples
14 |
15 | ```
16 | apio format # Format all source files.
17 | apio format -v # Format all files with verbose output.
18 | apio format main.v main_tb.v # Format the two files.
19 | ```
20 |
21 |
Options
22 |
23 | ```
24 | -e, --env name Use a named environment from apio.ini
25 | -p, --project-dir path Specify the project root directory
26 | -v, --verbose Show detailed output
27 | -h, --help Show help message and exit
28 | ```
29 |
30 |
Customization
31 |
32 | The format command utilizes the format tool from the Verible project,
33 | which can be configured using the `format-verible-options` setting in `apio.ini`. For example:
34 |
35 | ```
36 | format-verible-options =
37 | --column_limit=80
38 | --indentation_spaces=4
39 | ```
40 |
41 | For a full list of Verible formatter flags, refer to the documentation
42 | page online or use the command `apio raw -- verible-verilog-format --helpfull`.
43 |
44 |
Protecting code
45 |
46 | Sections of source code can be protected from formatting using the Verible formatter directives:
47 |
48 | ```
49 | // verilog_format: off
50 | ... untouched code ...
51 | // verilog_format: on
52 | ```
53 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_devices.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio devices" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.common.apio_console import cunstyle
5 | from apio.commands.apio import apio_top_cli as apio
6 |
7 |
8 | def test_devices(apio_runner: ApioRunner):
9 | """Test "apio devices" """
10 |
11 | with apio_runner.in_sandbox() as sb:
12 |
13 | # -- Execute "apio devices"
14 | result = sb.invoke_apio_cmd(apio, ["devices"])
15 | sb.assert_result_ok(result)
16 | assert "apio devices usb" in cunstyle(result.output)
17 | assert "apio devices serial" in cunstyle(result.output)
18 |
19 |
20 | def test_apio_devices(apio_runner: ApioRunner):
21 | """Test "apio devices usb|serial" """
22 |
23 | with apio_runner.in_sandbox() as sb:
24 |
25 | # -- Execute "apio devices usb". We run it in a
26 | # -- subprocess such that it releases the libusb1 file it uses.
27 | # -- This also means that it's not included in the pytest test
28 | # -- coverage report.
29 | result = sb.invoke_apio_cmd(
30 | apio, ["devices", "usb"], in_subprocess=True
31 | )
32 | sb.assert_result_ok(result)
33 | print(result.output)
34 |
35 | # -- Execute "apio devices serial". We run it in a
36 | # -- subprocess such that it releases the libusb1 file it uses.
37 | # -- This also means that it's not included in the pytest test
38 | # -- coverage report.
39 | result = sb.invoke_apio_cmd(
40 | apio, ["devices", "serial"], in_subprocess=True
41 | )
42 | sb.assert_result_ok(result)
43 | print(result.output)
44 |
--------------------------------------------------------------------------------
/.github/workflows/resources/apio-pyinstaller.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python ; coding: utf-8 -*-
2 |
3 | # Pyinstaller spec file for apio package generation.
4 |
5 | from PyInstaller.utils.hooks import collect_submodules
6 | import apio
7 | import sys
8 | from pathlib import Path
9 |
10 | # -- Get the path to the 'apio' dir of the apio package.
11 | apio_dir = Path(sys.modules["apio"].__file__).parent
12 | print(f"{apio_dir=}")
13 |
14 | added_files = [
15 | ( apio_dir / 'resources/*', 'apio/resources' ),
16 | ( apio_dir / 'scons/SConstruct', 'apio/scons' ),
17 | ]
18 |
19 | hiddenimports = (
20 | collect_submodules('SCons') +
21 | collect_submodules('apio') +
22 | collect_submodules('usb')
23 | )
24 |
25 | # Per https://github.com/orgs/pyinstaller/discussions/9148
26 | excludes = [ 'pkg_resources' ]
27 |
28 | a = Analysis(
29 | [apio_dir / '__main__.py'],
30 | pathex=[],
31 | binaries=[],
32 | datas=added_files,
33 | hiddenimports=hiddenimports,
34 | hookspath=[],
35 | hooksconfig={},
36 | runtime_hooks=[],
37 | excludes=excludes,
38 | noarchive=False,
39 | optimize=0,
40 | )
41 | pyz = PYZ(a.pure)
42 |
43 | exe = EXE(
44 | pyz,
45 | a.scripts,
46 | [],
47 | exclude_binaries=True,
48 | name='apio',
49 | debug=False,
50 | bootloader_ignore_signals=False,
51 | strip=False,
52 | upx=True,
53 | console=True,
54 | disable_windowed_traceback=False,
55 | argv_emulation=False,
56 | target_arch=None,
57 | codesign_identity=None,
58 | entitlements_file=None,
59 | )
60 | coll = COLLECT(
61 | exe,
62 | a.binaries,
63 | a.datas,
64 | strip=False,
65 | upx=True,
66 | upx_exclude=[],
67 | name='apio',
68 | )
69 |
--------------------------------------------------------------------------------
/docs/cmd-apio-devices.md:
--------------------------------------------------------------------------------
1 | # Apio devices
2 |
3 | ---
4 |
5 | ## apio devices
6 |
7 | The `apio devices` command group lists devices connected to your computer.
8 | It is mainly used for diagnosing connectivity or driver issues.
9 |
10 |
Options
11 |
12 | ```
13 | -h, --help Show this message and exit.
14 | ```
15 |
16 |
Subcommands
17 |
18 | ```
19 | apio devices usb
20 | apio devices serial
21 | ```
22 |
23 | ---
24 |
25 | ## apio devices usb
26 |
27 | The command `apio devices usb` displays the USB devices currently
28 | connected to your computer. It is useful for diagnosing FPGA board
29 | connectivity issues.
30 |
31 |
Examples
32 |
33 | ```
34 | apio devices usb # List USB devices.
35 | ```
36 |
37 |
Options
38 |
39 | ```
40 | -h, --help Show this message and exit.
41 | ```
42 |
43 | Example output
44 |
45 | 
46 |
47 | ---
48 |
49 | ## apio devices serial
50 |
51 | The command `apio devices serial` displays the serial devices
52 | currently connected to your computer. It is useful for diagnosing FPGA
53 | board connectivity issues.
54 |
55 |
Examples
56 |
57 | ```
58 | apio devices serial # List serial devices.
59 | ```
60 |
61 |
Options
62 |
63 | ```
64 | -h, --help Show this message and exit.
65 | ```
66 |
67 |
Notes
68 |
69 | - Devices like the FTDI FT2232 with multiple channels may appear as
70 | multiple entries—one per serial port.
71 |
72 | - On Windows, manufacturer and product strings of FTDI based devices may
73 | show their FTDI generic values rather than the custom values such as 'Alhambra II' set by the device manufacturer.
74 |
75 | Example output
76 |
77 | 
78 |
--------------------------------------------------------------------------------
/apio/resources/config.jsonc:
--------------------------------------------------------------------------------
1 | // General config parameters.
2 | //
3 | {
4 | // How many days we keep a cached remote config before we fetch a new onw.
5 | // Value of 1 means fetching once a day. 7 once every seven days, and so on.
6 | "remote-config-ttl-days" : 1,
7 |
8 | // The time period in minutes after a config fetch failure until the next
9 | // try. This applies only for optional remote config fetch where Apio can
10 | // fall back to the cached config, and does not apply to cases where a
11 | // fresh config is required, e.g. by 'apio packages update' or if a cached
12 | // config is not available.
13 | //
14 | // To force an immediate config fetch, user can run 'apio packages update'.
15 | "remote-config-retry-minutes": 60,
16 |
17 | // URL of the apio remote config file. The placeholder {major} and {minor}
18 | // are replaced with Apio's major and minor version number with no zero
19 | // padding. For example, for apio 1.12.3, {major} is "1" and {minor} is "12".
20 | //
21 | //
22 | // The value of the remote config field here can be overridden for testing
23 | // using the env var APIO_REMOTE_CONFIG_URL, including for reading from
24 | // a local file using the "file://" protocol spec.
25 | //
26 | // For testing using the remote config at zapta:
27 | // export APIO_REMOTE_CONFIG_URL="https://github.com/zapta/apio/raw/develop/remote-config/apio-{major}.{minor}.x.jsonc"
28 | //
29 | // For testing using the local remote config
30 | // export APIO_REMOTE_CONFIG_URL="file:///projects/apio-dev/repo/remote-config/apio-{major}.{minor}.x.jsonc"
31 |
32 | "remote-config-url": "https://github.com/fpgawars/apio/raw/develop/remote-config/apio-{major}.{minor}.x.jsonc"
33 | }
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/apio/resources/packages.jsonc:
--------------------------------------------------------------------------------
1 | // Definition of apio software packages. These are packages with various
2 | // data and utilities that are installed by apio.
3 | //
4 | // Additional packages information is fetched dynamically from the remote
5 | // config and is saved in .apio/profile.json.
6 | //
7 | // TODO: Delete "file_name" and "extension". They move to the remote config.
8 | {
9 | // "definitions" package
10 | "definitions": {
11 | "description": "Apio definitions",
12 | "env": {}
13 | },
14 | // "examples" package
15 | "examples": {
16 | "description": "Apio project examples",
17 | "env": {}
18 | },
19 | // "oss-cad-suite" package
20 | "oss-cad-suite": {
21 | "description": "Yosys HQ oss-cad-suite",
22 | "env": {
23 | "path": [
24 | "%p/bin",
25 | "%p/lib"
26 | ],
27 | "vars": {
28 | "VERILATOR_ROOT": "%p/share/verilator",
29 | "ICEBOX": "%p/share/icebox",
30 | "TRELLIS": "%p/share/trellis",
31 | "YOSYS_LIB": "%p/share/yosys"
32 | }
33 | }
34 | },
35 | // "graphviz" package
36 | "graphviz": {
37 | "restricted-to-platforms": [
38 | "windows-amd64"
39 | ],
40 | "description": "Graphviz tool for Windows",
41 | "env": {
42 | "path": [
43 | "%p/bin"
44 | ]
45 | }
46 | },
47 | // "verible" package
48 | "verible": {
49 | "description": "Chips Aliance Verible toolset",
50 | "env": {
51 | "path": [
52 | "%p/bin"
53 | ]
54 | }
55 | },
56 | // "drivers" package
57 | "drivers": {
58 | "restricted-to-platforms": [
59 | "windows-amd64"
60 | ],
61 | "description": "Drivers tools for Windows",
62 | "env": {
63 | "path": [
64 | "%p/bin"
65 | ]
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/docs/command-line.md:
--------------------------------------------------------------------------------
1 | # Apio command line
2 |
3 | This page explains the principles and general options of using the Apio command line. For
4 | specific commands, see the respective command description.
5 |
6 | ---
7 |
8 | ## Apio command tree
9 |
10 | Apio commands are organized as a command tree rooted at `apio`. Some commands, such as `apio build`, have only two levels, while others, like `apio drivers install ftdi`, have three or more. To explore available commands at each level, use the `-h` option (short for `--help`) with any command.
11 |
12 | For example
13 |
14 | ```
15 | apio -h
16 | apio info -h
17 | apio info cli -h
18 | ```
19 |
20 | ---
21 |
22 | ## Apio command options
23 |
24 | Most Apio commands have options that let you control their behavior. For example, the command 'apio build' has options to control the verbosity of its output:
25 |
26 | ```
27 | apio build --verbose
28 | apio build --verbose-synth
29 | ```
30 |
31 | To list the options for a command, run it with the `-h` option. For example:
32 |
33 | ```
34 | apio build -h
35 | ```
36 |
37 | ---
38 |
39 | ## Apio command shortcuts
40 |
41 | When typing apio commands, it's sufficient to type enough of each command to make the selection unambiguous. For example, these commands below are equivalent.
42 |
43 | ```
44 | apio preferences
45 | apio pref
46 | apio pr
47 | ```
48 |
49 | However, `apio p` is ambiguous because it matches both `apio preferences` and `apio packages`.
50 |
51 | ---
52 |
53 | ## Apio shell auto completion
54 |
55 | Apio's command line processor is based on the Python Click package which supports auto completion with some shells. Although it worked as a proof of concept, this feature is experimental and not guaranteed to function reliably. More information is available in the Click documentation:
56 |
--------------------------------------------------------------------------------
/docs/cmd-apio-drivers.md:
--------------------------------------------------------------------------------
1 | # Apio drivers
2 |
3 | ---
4 |
5 | ## apio drivers
6 |
7 | The `apio drivers` command group installs and uninstalls drivers needed by some FPGA boards.
8 |
9 |
Options
10 |
11 | ```
12 | -h, --help Show this message and exit.
13 | ```
14 |
15 |
32 |
33 | ```
34 | apio packages update # Update packages
35 | apio pack upd # Same, with shortcuts
36 | apio packages update --force # Force reinstallation from scratch
37 | apio packages update --verbose # Provide additional info
38 | ```
39 |
40 |
Options
41 |
42 | ```
43 | -f, --force Force reinstallation.
44 | -v, --verbose Show detailed output.
45 | -h, --help Show this message and exit.
46 | ```
47 |
48 |
Notes
49 |
50 | - Adding the `--force` option forces the reinstallation of existing
51 | packages; otherwise, packages that are already installed correctly
52 | remain unchanged.
53 |
54 | - It is recommended to run the 'apio packages update' once in a
55 | while because it checks the Apio remote server for updated packages with potential fixes and new examples.
56 |
57 | ---
58 |
59 | ## apio packages list
60 |
61 | The `apio packages list` command displays the available and installed Apio packages. The list may vary depending on your operating system.
62 |
63 |
70 |
71 | ```
72 | -v, --verbose Show detailed output.
73 | -h, --help Show this message and exit.
74 | ```
75 |
--------------------------------------------------------------------------------
/apio/common/rich_lib_windows.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # -- This file is part of the Apio project
3 | # -- (C) 2016-2018 FPGAwars
4 | # -- Author Jesús Arroyo
5 | # -- License GPLv2
6 | # -- Derived from:
7 | # ---- Platformio project
8 | # ---- (C) 2014-2016 Ivan Kravets
9 | # ---- License Apache v2
10 | """Functions to workaround the rich library bugs when stdout is piped out
11 | on windows."""
12 |
13 | import sys
14 | import platform
15 | import rich.console
16 |
17 |
18 | def fix_windows_stdout_encoding() -> bool:
19 | """Called on the apio process (parent) to fix its output encoding.
20 | Safe to call on non windows platforms. Returns True if fixed."""
21 | # -- This takes care of the table graphic box.
22 | # -- See https://github.com/Textualize/rich/issues/3625
23 | if (
24 | platform.system().lower() == "windows"
25 | and sys.stdout.encoding != "utf-8"
26 | ):
27 | sys.stdout.reconfigure(encoding="utf-8")
28 | return True
29 | # -- Else.
30 | return False
31 |
32 |
33 | def apply_workaround():
34 | """Called on the scons (child) process side, when running on windows,
35 | to apply the the workaround for the rich library."""
36 |
37 | # For accessing rich.console._windows_console_features
38 | # pylint: disable=protected-access
39 |
40 | # -- This takes care of the table graphic box.
41 | # -- See https://github.com/Textualize/rich/issues/3625
42 | # sys.stdout.reconfigure(encoding=params.stdout_encoding)
43 |
44 | # This enables the colors.
45 | # See https://github.com/Textualize/rich/issues/3082
46 |
47 | # -- Make sure that _windows_console_features is initialized with cached
48 | # -- values.
49 | rich.console.get_windows_console_features()
50 | assert rich.console._windows_console_features is not None
51 |
52 | # -- Apply the patch.
53 | rich.console._windows_console_features.vt = True
54 | rich.console._windows_console_features.truecolor = True
55 |
--------------------------------------------------------------------------------
/docs/installing-apio-ide.md:
--------------------------------------------------------------------------------
1 | # Installing Apio IDE
2 |
3 | To install Apio IDE, follow these steps
4 |
5 | 1. If you don't have already Visual Studio Code install, download and install it from its [official site](https://code.visualstudio.com/download)
6 | 1. Start Visual Studio Code.
7 | 1. In the Extension tab on the left, search and install the extension `fpgawars.apio1` (**Apio FPGA**).
8 | 1. This concludes the installation of the Apio IDE. The screenshot below shows the components of the Apio IDE.
9 |
10 | > HINT: Once you start using Apio IDE, it will automatically install the Apio CLI in the `.apio/bin` directory under your home
11 | > directory. You can add that directory to your `PATH` if you want to use the Apio CLI `apio ...` commands also outside of VS Code.
12 |
13 |
14 | 
15 |
16 | | Part | Condition | Function |
17 | | ------------------------ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
18 | | [1] Apio project buttons | Appears when an Apio project is open. | Provides quick access to common project functions. |
19 | | [2] Apio extension tab | Appears when the Apio extensions is installed. | Opens the Apio side bar with the available commands. |
20 | | [3] Apio side bar | Appears when the apio extension tab is selected. | Provides access to all Apio functions. |
21 | | [4] Apio terminal | Appears when an Apio CLI command is executed or when **TOOLS → misc → apio shell** is selected. | Shows the output of Apio CLI commands and provides the interactive Apio shell. |
22 |
--------------------------------------------------------------------------------
/scripts/generate_commands_help.py:
--------------------------------------------------------------------------------
1 | """A python script to generate COMMANDS.md."""
2 |
3 | import sys
4 | import json
5 | from typing import List
6 | import subprocess
7 |
8 |
9 | def extract_command_list(node: dict, node_path: List[str]) -> list[List[str]]:
10 | """Recursively extract the commands from the json commands tree."""
11 |
12 | # -- Add the node to the list.
13 | result = [node_path]
14 |
15 | # -- Simple command, no children
16 | if "commands" not in node:
17 | return result
18 |
19 | for command, command_dict in node["commands"].items():
20 | command_path = node_path + [command]
21 | commands = extract_command_list(command_dict, command_path)
22 | result.extend(commands)
23 |
24 | # result = sorted(result)
25 | return result
26 |
27 |
28 | def get_commands_list() -> List[str]:
29 | """Run 'apio apio get-commands' and extract the commands list."""
30 |
31 | # -- Get the command hierarchy as a JSON doc.
32 | result = subprocess.run(
33 | ["apio", "api", "get-commands"],
34 | capture_output=True,
35 | text=True,
36 | check=True,
37 | encoding="utf-8",
38 | )
39 |
40 | data = json.loads(result.stdout)
41 | commands = extract_command_list(data["commands"]["apio"], ["apio"])
42 |
43 | return commands
44 |
45 |
46 | def main():
47 | """Get the apio command list and dump their --help text."""
48 | commands = get_commands_list()
49 |
50 | commands = sorted(commands)
51 |
52 | for command in commands:
53 | command_str = " ".join(command)
54 | print(command_str, file=sys.stderr)
55 |
56 | result = subprocess.run(
57 | command + ["-h"],
58 | capture_output=True,
59 | text=True,
60 | check=True,
61 | encoding="utf-8",
62 | )
63 |
64 | print("\n--------------------------------------------\n")
65 | print(command_str)
66 | print()
67 | print(result.stdout)
68 |
69 |
70 | if __name__ == "__main__":
71 | main()
72 |
--------------------------------------------------------------------------------
/docs/cmd-apio-sim.md:
--------------------------------------------------------------------------------
1 | # Apio sim
2 |
3 | ---
4 |
5 | ## apio sim
6 |
7 | The `apio sim` command runs a simulation for the default or specified
8 | testbench and displays the results in a GTKWave window. Testbench files
9 | should end with `_tb`, such as `main_tb.v` or `main_tb.sv`. You can set
10 | the default testbench using the `default-testbench` option in `apio.ini`.
11 | If this option is not set and there's only one testbench in the project,
12 | that file will be used.
13 |
14 | The `apio sim` command defines the macro `APIO_SIM=1`, which allows failed
15 | assertions to skip the `$fatal` call. This lets the simulation continue and
16 | display faulty signals in the GTKWave viewer.
17 |
18 | ```
19 | # Instead of this
20 | $fatal;
21 |
22 | # Use this
23 | if (!`APIO_SIM) $fatal;
24 | ```
25 |
26 |
37 |
38 | ```
39 | -f, --force Force simulation
40 | -e, --env name Use a named environment from apio.ini
41 | -n, --no-gtkwave Skip GTKWave
42 | -d, --detach Launch and forget GTKWave.
43 | -p, --project-dir path Specify the project root directory
44 | -h, --help Show help message and exit
45 | ```
46 |
47 |
Notes
48 |
49 | - Avoid using the Verilog `$dumpfile()` function, as it can override the default name and location Apio assigns for the `.vcd` file.
50 |
51 | - Testbench paths must always be relative to the project directory, even when using `--project-dir`.
52 |
53 | - For a sample testbench that utilizes this macro, see the apio example `alhambra-ii/getting-started`.
54 |
55 | - When configuring signals in GTKWave, save your setup so you don’t have to repeat it each time.
56 |
57 |
Example simulation results
58 |
59 | 
60 |
--------------------------------------------------------------------------------
/apio/commands/apio_docs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # -- This file is part of the Apio project
3 | # -- (C) 2016-2024 FPGAwars
4 | # -- Authors
5 | # -- * Jesús Arroyo (2016-2019)
6 | # -- * Juan Gonzalez (obijuan) (2019-2024)
7 | # -- License GPLv2
8 | """Implementation of 'apio format' command"""
9 |
10 | import sys
11 | import webbrowser
12 | import click
13 | from apio.common.apio_console import cout
14 | from apio.apio_context import (
15 | ApioContext,
16 | PackagesPolicy,
17 | ProjectPolicy,
18 | RemoteConfigPolicy,
19 | )
20 | from apio.utils import cmd_util
21 |
22 |
23 | # -------------- apio format
24 |
25 | # -- Text in the rich-text format of the python rich library.
26 | APIO_DOCS_HELP = """
27 | The command 'apio docs' opens the Apio documentation using the user's \
28 | default browser.
29 |
30 | Examples:[code]
31 | apio docs # Open the apio docs.
32 | apio docs --commands # Land on the commands list page.
33 | """
34 |
35 | commands_option = click.option(
36 | "commands", # Var name.
37 | "-c",
38 | "--commands",
39 | is_flag=True,
40 | help="Show the commands page.",
41 | cls=cmd_util.ApioOption,
42 | )
43 |
44 |
45 | @click.command(
46 | name="docs",
47 | cls=cmd_util.ApioCommand,
48 | short_help="Show Apio documentation.",
49 | help=APIO_DOCS_HELP,
50 | )
51 | @commands_option
52 | def cli(
53 | # Options
54 | commands,
55 | ):
56 | """Implements the docs command which opens the apio documentation in
57 | the default browser.
58 | """
59 |
60 | # -- Create an apio context with a project object.
61 | _ = ApioContext(
62 | project_policy=ProjectPolicy.NO_PROJECT,
63 | remote_config_policy=RemoteConfigPolicy.CACHED_OK,
64 | packages_policy=PackagesPolicy.IGNORE_PACKAGES,
65 | )
66 |
67 | url = "https://fpgawars.github.io/apio/docs"
68 |
69 | if commands:
70 | url += "/commands-list"
71 |
72 | cout(f"URL: {url}")
73 | cout("Opening default browser")
74 |
75 | default_browser = webbrowser.get()
76 | default_browser.open(url)
77 |
78 | sys.exit(0)
79 |
--------------------------------------------------------------------------------
/docs/updating-the-docs.md:
--------------------------------------------------------------------------------
1 | # Updating Apio Docs
2 |
3 | Apio documentation is written in Markdown and published using the `mkdocs`
4 | tool at . The rest of this page explains
5 | how to update and preview the documentation.
6 |
7 | > NOTE: The Apio github workflow that publishes the Apio docs, also publishes the
8 | > test coverage report at
9 |
10 | ## Installing MkDocs
11 |
12 | Install MkDocs and the Material theme with:
13 |
14 | ```
15 | pip install mkdocs-material
16 | ```
17 |
18 | ## Navigation
19 |
20 | The structure and navigation of the docs are defined in `mkdocs.yml`,
21 | including the site layout and page mappings.
22 |
23 | ## Pages
24 |
25 | Markdown page files (`*.md`) are stored in the `docs` directory.
26 |
27 | ## Graphics
28 |
29 | Pictures, diagrams, and other graphics are stored in the `docs/assets`
30 | directory.
31 |
32 | ## Stylesheets
33 |
34 | Apio's custom styles are defined in `docs/stylesheets/extra.css`, which is
35 | referenced from `mkdocs.yml`.
36 |
37 | ## Previewing Local Changes
38 |
39 | To start a local web server and preview changes as you edit:
40 |
41 | ```
42 | invoke docs-viewer
43 | ```
44 |
45 | This enables live reloading in your browser.
46 |
47 | ## Sending a Pull Request
48 |
49 | Before sending a pull request to the Apio repository, check the following on your forked repository:
50 |
51 | 1. The following workflows in the **Actions** tab of your fork repo completed successfully:
52 |
53 | - **publish-mkdocs-docs**
54 | - **pages-build-deployment**
55 | - **monitor-apio-latest**
56 | - **test**
57 |
58 | 2. The docs at `https://${user}.github.io/apio/docs` are live and include
59 | your changes (replace _${user}_ with the username of your fork repo).
60 |
61 | ## Publishing
62 |
63 | Documentation is automatically published when changes are pushed
64 | to `mkdocs.yml` or the `docs` directory. This triggers the GitHub Actions
65 | workflow:
66 |
67 | ```
68 | .github/workflows/publish-mkdocs-docs.yaml
69 | ```
70 |
71 | The workflow updates the site on GitHub Pages via the `gh-pages`
72 | branch. You can monitor workflow runs in the repository's **Actions** tab.
73 |
--------------------------------------------------------------------------------
/tests/unit_tests/utils/test_cmd_util.py:
--------------------------------------------------------------------------------
1 | """
2 | Tests of cmd_util.py
3 | """
4 |
5 | import sys
6 | import pytest
7 | import click
8 | from apio.utils.cmd_util import (
9 | ApioOption,
10 | ApioCmdContext,
11 | check_at_most_one_param,
12 | )
13 |
14 | # TODO: Add more tests.
15 |
16 |
17 | # -- A fake command for testing.
18 | @click.command(name="fake_cmd")
19 | @click.option("_opt1", "--opt1", is_flag=True, cls=ApioOption)
20 | @click.option("_opt2", "--opt2", is_flag=True, cls=ApioOption)
21 | @click.option("_opt3", "--opt3", is_flag=True, cls=ApioOption)
22 | @click.option("_opt4", "--opt4", is_flag=True, cls=ApioOption)
23 | def fake_cmd(_opt1, _opt2, _opt3, _opt4):
24 | """Fake click command for testing."""
25 | sys.exit(0)
26 |
27 |
28 | def test_check_at_most_one_param(capsys):
29 | """Tests the check_at_most_one_param() function."""
30 |
31 | # -- No option is specified. Should be ok.
32 | cmd_ctx = ApioCmdContext(fake_cmd)
33 | fake_cmd.parse_args(cmd_ctx, [])
34 | check_at_most_one_param(cmd_ctx, ["_opt1", "_opt2", "_opt3"])
35 |
36 | # -- One option is specified. Should be ok.
37 | cmd_ctx = ApioCmdContext(fake_cmd)
38 | fake_cmd.parse_args(cmd_ctx, ["--opt1"])
39 | check_at_most_one_param(cmd_ctx, ["_opt1", "_opt2", "_opt3"])
40 |
41 | # -- Another option is specified. Should be ok.
42 | cmd_ctx = ApioCmdContext(fake_cmd)
43 | fake_cmd.parse_args(cmd_ctx, ["--opt2"])
44 | check_at_most_one_param(cmd_ctx, ["_opt1", "_opt2", "_opt3"])
45 |
46 | # -- Two options but one is non related to the check. Should be ok.
47 | cmd_ctx = ApioCmdContext(fake_cmd)
48 | fake_cmd.parse_args(cmd_ctx, ["--opt1", "--opt4"])
49 | check_at_most_one_param(cmd_ctx, ["_opt1", "_opt2", "_opt3"])
50 |
51 | # -- Two options are specifies. Should fail.
52 | cmd_ctx = ApioCmdContext(fake_cmd)
53 | fake_cmd.parse_args(cmd_ctx, ["--opt1", "--opt2"])
54 | capsys.readouterr() # Reset capture.
55 | with pytest.raises(SystemExit) as e:
56 | check_at_most_one_param(cmd_ctx, ["_opt1", "_opt2", "_opt3"])
57 | captured = capsys.readouterr()
58 | assert e.value.code == 1
59 | assert "--opt1 and --opt2 cannot be combined together" in captured.out
60 |
--------------------------------------------------------------------------------
/docs/cmd-apio-examples.md:
--------------------------------------------------------------------------------
1 | # Apio examples
2 |
3 | ---
4 |
5 | ## apio examples
6 |
7 | The `apio examples` command group includes subcommands for listing and
8 | fetching example projects provided by Apio. Each example is a
9 | self-contained project that can be built and uploaded to its respective FPGA board.
10 |
11 | > The apio project examples are included in the apio 'examples' package
12 | > which is updated periodically.
13 |
14 |
Examples
15 |
16 | ```
17 | -h, --help Show this message and exit.
18 | ```
19 |
20 |
Subcommands
21 |
22 | ```
23 | apio examples list
24 | apio examples fetch
25 | ```
26 |
27 | ---
28 |
29 | ## apio examples list
30 |
31 | The `apio examples list` command shows available Apio project examples.
32 |
33 | > The apio project examples are included in the apio 'examples' package
34 | > which is updated periodically.
35 |
36 |
Examples
37 |
38 | ```
39 | apio examples list # List all examples
40 | apio examples list -v # Verbose output
41 | apio examples list | grep alhambra-ii # Filter for alhambra-ii examples
42 | apio examples list | grep -i blink # Filter for blinking examples
43 | apio examples list --docs # Use Apio docs format.
44 | ```
45 |
46 |
Options
47 |
48 | ```
49 | -v, --verbose Show detailed output.
50 | -d, --docs Format for Apio Docs.
51 | -h, --help Show this message and exit.
52 | ```
53 |
54 | ---
55 |
56 | ## apio examples fetch
57 |
58 | The `apio examples fetch` command retrieves a single example or all examples
59 | for a specific board. The default destination directory is the current directory and it can be overriden using the `--dst` flag. If the
60 | destination directory already exists, it must be empty.
61 |
62 | > The apio project examples are included in the apio 'examples' package
63 | > which is updated periodically.
64 |
65 |
Examples
66 |
67 | ```
68 | apio examples fetch alhambra-ii/ledon # Single example
69 | apio examples fetch alhambra-ii # All examples for the board
70 | apio examples fetch alhambra-ii -d work # Explicit destination
71 | ```
72 |
73 |
Options
74 |
75 | ```
76 | -d, --dst path Set a different destination directory.
77 | -h, --help Show this message and exit.
78 | ```
79 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_raw.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio raw" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 |
7 | def test_raw(apio_runner: ApioRunner):
8 | """Test "apio raw" with different parameters"""
9 |
10 | with apio_runner.in_sandbox() as sb:
11 |
12 | # -- NOTE: We run the apio raw command in a sub process to have a
13 | # -- proper sys.argv for it for the '--' separator validation.
14 |
15 | # -- Execute "apio raw"
16 | result = sb.invoke_apio_cmd(apio, ["raw"], in_subprocess=True)
17 | assert result.exit_code != 0, result.output
18 | assert (
19 | "at least one of --verbose or COMMAND must be specified"
20 | in result.output
21 | )
22 |
23 | # -- Execute "apio raw -v"
24 | result = sb.invoke_apio_cmd(apio, ["raw", "-v"], in_subprocess=True)
25 | sb.assert_result_ok(result)
26 | assert "Environment settings:" in result.output
27 | assert "PATH" in result.output
28 | assert "YOSYS_LIB" in result.output
29 |
30 | # -- Run 'apio raw "nextpnr-ice40 --help"'.
31 | result = sb.invoke_apio_cmd(
32 | apio, ["raw", "--", "nextpnr-ice40", "--help"], in_subprocess=True
33 | )
34 | sb.assert_result_ok(result, bad_words=[])
35 |
36 | # -- Run a command without the required '--'
37 | result = sb.invoke_apio_cmd(
38 | apio, ["raw", "nextpnr-ice40"], in_subprocess=True
39 | )
40 | assert result.exit_code != 0, result.output
41 | assert "command separator '--' was not found" in result.output
42 |
43 | # -- Run a command with a token before the '--' separator.
44 | result = sb.invoke_apio_cmd(
45 | apio, ["raw", "nextpnr-ice40", "--", "--help"], in_subprocess=True
46 | )
47 | assert result.exit_code != 0, result.output
48 | assert "Invalid arguments: ['nextpnr-ice40']" in result.output
49 |
50 | # -- Run 'apio raw -v'
51 | result = sb.invoke_apio_cmd(apio, ["raw", "-v"], in_subprocess=True)
52 | sb.assert_result_ok(result)
53 | assert "Environment settings:" in result.output
54 | assert "YOSYS_LIB" in result.output
55 |
--------------------------------------------------------------------------------
/remote-config/apio-0.9.7.jsonc:
--------------------------------------------------------------------------------
1 | // Remote config file for Apio 0.9.7
2 | //
3 | // Supported vars (see installer.py for details):
4 | //
5 | // ${PLATFORM} - platform tag (from platforms.jsonc)
6 | // ${YYYY-MM-DD} - version as YYYY-MM-DD
7 | // ${YYYYMMDD} - version as YYYYMMDD
8 | //
9 | // NOTE: Github has a cache propagation of about 1 min between the
10 | // time this file is submitted and until it's available for download.
11 | {
12 | "packages": {
13 | // -- Examples
14 | "examples": {
15 | "repository": {
16 | "organization": "fpgawars",
17 | "name": "apio-examples"
18 | },
19 | "release": {
20 | "version": "2025.07.11",
21 | "release-tag": "${YYYY-MM-DD}",
22 | "package-file": "apio-examples-${YYYYMMDD}.tgz"
23 | }
24 | },
25 | // -- OSS cad suite
26 | "oss-cad-suite": {
27 | "repository": {
28 | "organization": "fpgawars",
29 | "name": "tools-oss-cad-suite"
30 | },
31 | "release": {
32 | "version": "2025.07.07",
33 | "release-tag": "${YYYY-MM-DD}",
34 | "package-file": "apio-oss-cad-suite-${PLATFORM}-${YYYYMMDD}.tgz"
35 | }
36 | },
37 | // -- Graphviz
38 | "graphviz": {
39 | "repository": {
40 | "organization": "fpgawars",
41 | "name": "tools-graphviz"
42 | },
43 | "release": {
44 | "version": "2025.06.13",
45 | "release-tag": "${YYYY-MM-DD}",
46 | "package-file": "apio-graphviz-${PLATFORM}-${YYYYMMDD}.tgz"
47 | }
48 | },
49 | // -- Verible
50 | "verible": {
51 | "repository": {
52 | "organization": "fpgawars",
53 | "name": "tools-verible"
54 | },
55 | "release": {
56 | "version": "2025.06.13",
57 | "release-tag": "${YYYY-MM-DD}",
58 | "package-file": "apio-verible-${PLATFORM}-${YYYYMMDD}.tgz"
59 | }
60 | },
61 | // -- Drivers
62 | "drivers": {
63 | "repository": {
64 | "organization": "fpgawars",
65 | "name": "tools-drivers"
66 | },
67 | "release": {
68 | "version": "2025.06.13",
69 | "release-tag": "${YYYY-MM-DD}",
70 | "package-file": "apio-drivers-${PLATFORM}-${YYYYMMDD}.tgz"
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/remote-config/apio-1.0.0.jsonc:
--------------------------------------------------------------------------------
1 | // Remote config file for Apio 1.0.0
2 | //
3 | // Supported vars (see installer.py for details):
4 | //
5 | // ${PLATFORM} - platform tag (from platforms.jsonc)
6 | // ${YYYY-MM-DD} - version as YYYY-MM-DD
7 | // ${YYYYMMDD} - version as YYYYMMDD
8 | //
9 | // NOTE: Github has a cache propagation of about 1 min between the
10 | // time this file is submitted and until it's available for download.
11 | {
12 | "packages": {
13 | // -- Examples
14 | "examples": {
15 | "repository": {
16 | "organization": "fpgawars",
17 | "name": "apio-examples"
18 | },
19 | "release": {
20 | "version": "2025.09.23",
21 | "release-tag": "${YYYY-MM-DD}",
22 | "package-file": "apio-examples-${YYYYMMDD}.tgz"
23 | }
24 | },
25 | // -- OSS cad suite
26 | "oss-cad-suite": {
27 | "repository": {
28 | "organization": "fpgawars",
29 | "name": "tools-oss-cad-suite"
30 | },
31 | "release": {
32 | "version": "2025.09.24",
33 | "release-tag": "${YYYY-MM-DD}",
34 | "package-file": "apio-oss-cad-suite-${PLATFORM}-${YYYYMMDD}.tgz"
35 | }
36 | },
37 | // -- Graphviz
38 | "graphviz": {
39 | "repository": {
40 | "organization": "fpgawars",
41 | "name": "tools-graphviz"
42 | },
43 | "release": {
44 | "version": "2025.06.13",
45 | "release-tag": "${YYYY-MM-DD}",
46 | "package-file": "apio-graphviz-${PLATFORM}-${YYYYMMDD}.tgz"
47 | }
48 | },
49 | // -- Verible
50 | "verible": {
51 | "repository": {
52 | "organization": "fpgawars",
53 | "name": "tools-verible"
54 | },
55 | "release": {
56 | "version": "2025.06.13",
57 | "release-tag": "${YYYY-MM-DD}",
58 | "package-file": "apio-verible-${PLATFORM}-${YYYYMMDD}.tgz"
59 | }
60 | },
61 | // -- Drivers
62 | "drivers": {
63 | "repository": {
64 | "organization": "fpgawars",
65 | "name": "tools-drivers"
66 | },
67 | "release": {
68 | "version": "2025.06.13",
69 | "release-tag": "${YYYY-MM-DD}",
70 | "package-file": "apio-drivers-${PLATFORM}-${YYYYMMDD}.tgz"
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | # Apio's project file.
2 | # Package version is set in apio/__init.py
3 |
4 | [build-system]
5 | requires = ["flit_core >=2,<4"]
6 | build-backend = "flit_core.buildapi"
7 |
8 | [tool.flit.metadata.urls]
9 | "Homepage" = "https://github.com/fpgawars/apio"
10 | "Documentation" = "https://fpgawars.github.io/apio/docs"
11 | "Bugs Tracker" = "https://github.com/fpgawars/apio/issues"
12 | "Wiki" = "https://github.com/fpgawars/apio/wiki"
13 | "Discussions" = "https://github.com/fpgawars/apio/discussions"
14 |
15 | [tool.flit.metadata]
16 | module = "apio"
17 | author = "Jesus Arroyo"
18 | author-email = "jesus.jkhlg@gmail.com "
19 | home-page = "https://github.com/fpgawars/apio"
20 | description-file = "README.md"
21 | classifiers=[
22 | 'Development Status :: 5 - Production/Stable',
23 | 'Environment :: Console',
24 | 'Intended Audience :: Developers',
25 | 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
26 | 'Programming Language :: Python',
27 | 'Natural Language :: English',
28 | 'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)',
29 | ]
30 | requires-python = ">=3.11"
31 | requires = [
32 | 'click==8.2.1',
33 | 'colorama==0.4.6',
34 | 'configobj==5.0.9',
35 | 'debugpy==1.8.19',
36 | 'packaging==25.0',
37 | 'pyserial==3.5',
38 | 'requests==2.32.4',
39 | 'scons==4.8.1',
40 | 'semantic_version==2.10.0',
41 | 'wheel==0.45.1',
42 | 'blackiceprog==2.0.0',
43 | 'tinyfpgab==1.1.0',
44 | 'tinyprog==1.0.21',
45 | 'icefunprog==2.0.3',
46 | 'apycula==0.15',
47 | 'apollo_fpga==1.1.1',
48 | 'protobuf==6.33.0',
49 | 'rich==14.0.0',
50 | 'invoke==2.2.1'
51 | ]
52 |
53 | [tool.flit.sdist]
54 | # TODO: Revisit 'exclude`. What else we don't need in the package?
55 | exclude = ["tests/"]
56 |
57 | [tool.flit.metadata.requires-extra]
58 | blackiceprog = ['blackiceprog==2.0.0']
59 | litterbox = ['litterbox==0.2.2']
60 | tinyfpgab = ['tinyfpgab==1.1.0']
61 | icefunprog = ['icefunprog==2.0.3']
62 |
63 | [tool.flit.scripts]
64 | apio = "apio.__main__:main"
65 |
66 | [tool.black]
67 | line-length = 79
68 | target-version = ['py311']
69 |
70 | # NOTE: For pylint control see .pylintrc in this directory.
71 | # It allows controls that pyproject.toml doesn't support.
72 |
--------------------------------------------------------------------------------
/apio/commands/apio_report.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # -- This file is part of the Apio project
3 | # -- (C) 2016-2024 FPGAwars
4 | # -- Authors
5 | # -- * Jesús Arroyo (2016-2019)
6 | # -- * Juan Gonzalez (obijuan) (2019-2024)
7 | # -- License GPLv2
8 | """Implementation of 'apio' report' command"""
9 |
10 | import sys
11 | from typing import Optional
12 | from pathlib import Path
13 | import click
14 | from apio.managers.scons_manager import SConsManager
15 | from apio.commands import options
16 | from apio.apio_context import (
17 | ApioContext,
18 | PackagesPolicy,
19 | ProjectPolicy,
20 | RemoteConfigPolicy,
21 | )
22 | from apio.common.proto.apio_pb2 import Verbosity
23 | from apio.utils import cmd_util
24 |
25 |
26 | # ---------- apio report
27 |
28 | # -- Text in the rich-text format of the python rich library.
29 | APIO_REPORT_HELP = """
30 | The command 'apio report' provides information on the utilization and timing \
31 | of the design. It is useful for analyzing utilization bottlenecks and \
32 | verifying that the design can operate at the desired clock speed.
33 |
34 | Examples:[code]
35 | apio report # Print report.
36 | apio report --verbose # Print extra information.[/code]
37 | """
38 |
39 |
40 | @click.command(
41 | name="report",
42 | cls=cmd_util.ApioCommand,
43 | short_help="Report design utilization and timing.",
44 | help=APIO_REPORT_HELP,
45 | )
46 | @click.pass_context
47 | @options.env_option_gen()
48 | @options.project_dir_option
49 | @options.verbose_option
50 | def cli(
51 | _: click.Context,
52 | # Options
53 | env: Optional[str],
54 | project_dir: Optional[Path],
55 | verbose: bool,
56 | ):
57 | """Analyze the design and report timing."""
58 |
59 | # -- Create the apio context.
60 | apio_ctx = ApioContext(
61 | project_policy=ProjectPolicy.PROJECT_REQUIRED,
62 | remote_config_policy=RemoteConfigPolicy.CACHED_OK,
63 | packages_policy=PackagesPolicy.ENSURE_PACKAGES,
64 | project_dir_arg=project_dir,
65 | env_arg=env,
66 | )
67 |
68 | # -- Create the scons manager.
69 | scons = SConsManager(apio_ctx)
70 |
71 | # -- Create the verbosity params.
72 | verbosity = Verbosity(pnr=verbose)
73 |
74 | # Run scons
75 | exit_code = scons.report(verbosity)
76 |
77 | # -- Done!
78 | sys.exit(exit_code)
79 |
--------------------------------------------------------------------------------
/remote-config/apio-1.0.2.jsonc:
--------------------------------------------------------------------------------
1 | // Remote config file for Apio 1.0.2
2 | //
3 | // Supported vars (see installer.py for details):
4 | //
5 | // ${PLATFORM} - platform id (from platforms.jsonc)
6 | // ${YYYYMMDD} - the YYYY-MM_DD tag converted to YYYYMMDD
7 | //
8 | // NOTE: Github has a cache propagation of about 1 min between the
9 | // time this file is submitted and until it's available for download.
10 | {
11 | "packages": {
12 | // -- Definitions package
13 | "definitions": {
14 | "repository": {
15 | "organization": "fpgawars",
16 | "name": "apio-definitions"
17 | },
18 | "release": {
19 | "tag": "2025-10-20",
20 | "package": "apio-definitions-${YYYYMMDD}.tgz"
21 | }
22 | },
23 | // -- Examples package
24 | "examples": {
25 | "repository": {
26 | "organization": "fpgawars",
27 | "name": "apio-examples"
28 | },
29 | "release": {
30 | "tag": "2025-12-01",
31 | "package": "apio-examples-${YYYYMMDD}.tgz"
32 | }
33 | },
34 | // -- OSS Cad Suite package (Yosys)
35 | "oss-cad-suite": {
36 | "repository": {
37 | "organization": "fpgawars",
38 | "name": "tools-oss-cad-suite"
39 | },
40 | "release": {
41 | "tag": "2025-09-24",
42 | "package": "apio-oss-cad-suite-${PLATFORM}-${YYYYMMDD}.tgz"
43 | }
44 | },
45 | // -- Graphviz package
46 | "graphviz": {
47 | "repository": {
48 | "organization": "fpgawars",
49 | "name": "tools-graphviz"
50 | },
51 | "release": {
52 | "tag": "2025-06-13",
53 | "package": "apio-graphviz-${PLATFORM}-${YYYYMMDD}.tgz"
54 | }
55 | },
56 | // -- Verible package
57 | "verible": {
58 | "repository": {
59 | "organization": "fpgawars",
60 | "name": "tools-verible"
61 | },
62 | "release": {
63 | "tag": "2025-06-13",
64 | "package": "apio-verible-${PLATFORM}-${YYYYMMDD}.tgz"
65 | }
66 | },
67 | // -- Drivers package
68 | "drivers": {
69 | "repository": {
70 | "organization": "fpgawars",
71 | "name": "tools-drivers"
72 | },
73 | "release": {
74 | "tag": "2025-06-13",
75 | "package": "apio-drivers-${PLATFORM}-${YYYYMMDD}.tgz"
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/remote-config/apio-1.1.x.jsonc:
--------------------------------------------------------------------------------
1 | // Remote config file for Apio versions 1.1.x.
2 | //
3 | // Supported vars (see installer.py for details):
4 | //
5 | // ${PLATFORM} - platform id (from platforms.jsonc)
6 | // ${YYYYMMDD} - the YYYY-MM_DD tag converted to YYYYMMDD
7 | //
8 | // NOTE: Github has a cache propagation of about 1 min between the
9 | // time this file is submitted and until it's available for download.
10 | {
11 | "packages": {
12 | // -- Definitions package
13 | "definitions": {
14 | "repository": {
15 | "organization": "fpgawars",
16 | "name": "apio-definitions"
17 | },
18 | "release": {
19 | "tag": "2025-10-20",
20 | "package": "apio-definitions-${YYYYMMDD}.tgz"
21 | }
22 | },
23 | // -- Examples package
24 | "examples": {
25 | "repository": {
26 | "organization": "fpgawars",
27 | "name": "apio-examples"
28 | },
29 | "release": {
30 | "tag": "2025-12-23",
31 | "package": "apio-examples-${YYYYMMDD}.tgz"
32 | }
33 | },
34 | // -- OSS Cad Suite package (Yosys)
35 | "oss-cad-suite": {
36 | "repository": {
37 | "organization": "fpgawars",
38 | "name": "tools-oss-cad-suite"
39 | },
40 | "release": {
41 | "tag": "2025-09-24",
42 | "package": "apio-oss-cad-suite-${PLATFORM}-${YYYYMMDD}.tgz"
43 | }
44 | },
45 | // -- Graphviz package
46 | "graphviz": {
47 | "repository": {
48 | "organization": "fpgawars",
49 | "name": "tools-graphviz"
50 | },
51 | "release": {
52 | "tag": "2025-06-13",
53 | "package": "apio-graphviz-${PLATFORM}-${YYYYMMDD}.tgz"
54 | }
55 | },
56 | // -- Verible package
57 | "verible": {
58 | "repository": {
59 | "organization": "fpgawars",
60 | "name": "tools-verible"
61 | },
62 | "release": {
63 | "tag": "2025-06-13",
64 | "package": "apio-verible-${PLATFORM}-${YYYYMMDD}.tgz"
65 | }
66 | },
67 | // -- Drivers package
68 | "drivers": {
69 | "repository": {
70 | "organization": "fpgawars",
71 | "name": "tools-drivers"
72 | },
73 | "release": {
74 | "tag": "2025-06-13",
75 | "package": "apio-drivers-${PLATFORM}-${YYYYMMDD}.tgz"
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/docs/using-examples.md:
--------------------------------------------------------------------------------
1 | # Using Apio's Examples
2 |
3 | Apio comes with a set of sample projects that demonstrate its features and can be used as starting points for your own projects. To list the available examples, type:
4 |
5 | ```
6 | $ apio examples list
7 | ```
8 |
9 | ```
10 | ┌────────────────────────────────┬───────┬─────────────────────────────────────────────────────────────┐
11 | │ BOARD/EXAMPLE │ ARCH │ DESCRIPTION │
12 | ├────────────────────────────────┼───────┼─────────────────────────────────────────────────────────────┤
13 | │ alchitry-cu/blinky │ ice40 │ Blinking all LEDs │
14 | │ alhambra-ii/bcd-counter │ ice40 │ Verilog example with testbenches and subdirectories. │
15 | │ alhambra-ii/bcd-counter-sv │ ice40 │ SystemVerilog example with testbenches and subdirectories. │
16 | │ alhambra-ii/blinky │ ice40 │ Blinking LED │
17 | │ alhambra-ii/getting-started │ ice40 │ Example for Apio Getting Started docs. │
18 | │ alhambra-ii/ledon │ ice40 │ Turning on an LED │
19 | │ sipeed-tang-nano-9k/blinky-sv │ gowin │ Blinking LED (SystemVerilog) │
20 | │ sipeed-tang-nano-9k/pll │ gowin │ PLL clock multiplier │
21 | └────────────────────────────────┴───────┴─────────────────────────────────────────────────────────────┘
22 | ```
23 |
24 | To fetch an example we create a new empty directory and fetch the example files int it.
25 |
26 | ```
27 | # Create an empty project directory
28 | $ apio mkdir work
29 | $ cd work
30 |
31 | # Fetch the example
32 | $ apio example fetch alhambra-ii/getting-started
33 | ```
34 |
35 | Now lets look at the project file structure
36 |
37 | ```
38 | $ tree .
39 | .
40 | ├── apio.ini
41 | ├── main_tb.gtkw
42 | ├── main_tb.v
43 | ├── main.v
44 | └── pinout.pcf
45 | ```
46 |
47 | And the project file `apio.ini`.
48 | ```
49 | $ cat -n apio.ini
50 | 1 ; Apio project file.
51 | 2
52 | 3 [env:default]
53 | 4
54 | 5 ; Board ID
55 | 6 board = alhambra-ii
56 | 7
57 | 8 ; Top module name (in main.v)
58 | 9 top-module = Main
59 | ```
60 |
61 | The fetched example is now an Apio project that can be built and uploaded to a matching FPGA board.
62 |
--------------------------------------------------------------------------------
/.github/workflows/monitor-apio-prod.yaml:
--------------------------------------------------------------------------------
1 | # A periodic workflow to test that the latest dev version is functional.
2 | # It's not a through test by any means, more like a sanity test.
3 |
4 | name: monitor-apio-prod
5 |
6 | on:
7 | # Run on commit.
8 | # push:
9 |
10 | # Run every 6 hours
11 | schedule:
12 | - cron: '0 */6 * * *'
13 |
14 | # Allow manual launch
15 | workflow_dispatch:
16 |
17 | jobs:
18 | monitor:
19 | runs-on: ${{ matrix.os }}
20 |
21 | defaults:
22 | run:
23 | shell: bash
24 |
25 | strategy:
26 | matrix:
27 | # 'macos-latest' -> darwin apple silicon
28 | # 'macos-15-intel' -> darwin intel x86
29 | os: [ubuntu-22.04, macos-latest, macos-15-intel, windows-latest]
30 | python-version: ['3.11', '3.12', '3.13', "3.14"]
31 |
32 | steps:
33 | - name: Show architecture
34 | run: uname -a
35 |
36 | - name: Set up Python
37 | uses: actions/setup-python@v4
38 | with:
39 | python-version: ${{matrix.python-version}}
40 |
41 | - name: Show python version
42 | run: |
43 | python --version
44 |
45 | - name: Install apio from pypi
46 | run: |
47 | pip install apio
48 |
49 | - name: Load apio packages (slow)
50 | run: |
51 | apio install --all
52 |
53 | - name: Show apio info
54 | run: |
55 | apio system --info
56 |
57 | - name: Test ICE40 project
58 | run: |
59 | # Show commands
60 | set -x
61 |
62 | # Create a project
63 | mkdir ice40_project
64 | cd ice40_project
65 | apio examples --files Alhambra-II/Blinky
66 | ls -al
67 |
68 | # Run a few command
69 | # apio lint
70 | apio verify
71 | apio build
72 | apio time
73 | apio system --lsftdi
74 | apio system --lsusb
75 | apio system --lsserial
76 |
77 | find .
78 |
79 | # Check that a few files exists (fails if not)
80 | ls -al hardware.bin
81 | ls -al hardware.rpt
82 | ls -al hardware.out
83 |
--------------------------------------------------------------------------------
/scripts/genereate-repo-cleaner.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Skeleton script to for one time cleanup of repo's releases and tag.
4 | # It generates a bash script with commented out commands to delete each
5 | # release and each tag. Uncomment the ones you want to delete and run
6 | # the generated shell script.
7 |
8 | set -euo pipefail
9 |
10 | # ------------------------------------------------------------
11 | # Usage: ./repo-cleaner-gen.sh
12 | # Example: ./repo-cleaner-gen.sh zapta/apio-vscode
13 | # ------------------------------------------------------------
14 |
15 | if [[ $# -ne 1 ]]; then
16 | echo "Error: Please provide exactly one argument: owner/repo"
17 | echo "Example: $0 zapta/apio-vscode"
18 | exit 1
19 | fi
20 |
21 | REPO="$1"
22 |
23 | cat </dev/null || echo " → Release already gone or not accessible"
33 | }
34 |
35 | delete_tag() {
36 | local tag="\$1"
37 | echo "Deleting tag: \$tag"
38 | gh api --method DELETE "/repos/\$REPO/git/refs/tags/\$tag" --silent 2>/dev/null || echo " → Tag already gone or not accessible"
39 | }
40 |
41 | delete_release_and_tag() {
42 | local tag="\$1"
43 | delete_release "\$tag"
44 | delete_tag "\$tag"
45 | }
46 |
47 | echo "DRY RUN — No deletions will happen until you uncomment the lines below"
48 | echo "Repo: $REPO"
49 | echo "========================================================================"
50 | echo
51 | echo "# 1. Releases + their tags (release first, then tag)"
52 | echo
53 |
54 | EOF
55 |
56 | # Step 1: For every release → delete release + tag (in that order)
57 | gh release list --repo "$REPO" --limit 1000 --json tagName -q '.[].tagName' |
58 | sort -V |
59 | while read -r tag; do
60 | [[ -n "$tag" ]] || continue
61 | echo "# delete_release_and_tag \"$tag\""
62 | done
63 |
64 | # Step 2: All tags that are NOT attached to any release
65 | cat <<'EOF'
66 |
67 | echo
68 | echo "# 2. Orphaned / unused tags (no associated release)"
69 | echo
70 |
71 | EOF
72 |
73 | # Get all tags without a release
74 | comm -23 \
75 | <(gh api "repos/$REPO/tags?per_page=100" --paginate --jq '.[].name' | sort -u) \
76 | <(gh release list --repo "$REPO" --limit 1000 --json tagName -q '.[].tagName' | sort -u) |
77 | while read -r tag; do
78 | [[ -n "$tag" ]] && echo "# delete_tag \"$tag\""
79 | done
80 |
81 | cat <<'EOF'
82 |
83 | echo
84 | EOF
--------------------------------------------------------------------------------
/remote-config/apio-1.0.1.jsonc:
--------------------------------------------------------------------------------
1 | // Remote config file for Apio 1.0.1
2 | //
3 | // Supported vars (see installer.py for details):
4 | //
5 | // ${PLATFORM} - platform tag (from platforms.jsonc)
6 | // ${YYYY-MM-DD} - version as YYYY-MM-DD
7 | // ${YYYYMMDD} - version as YYYYMMDD
8 | //
9 | // NOTE: Github has a cache propagation of about 1 min between the
10 | // time this file is submitted and until it's available for download.
11 | {
12 | "packages": {
13 | // -- Definitions
14 | "definitions": {
15 | "repository": {
16 | "organization": "fpgawars",
17 | "name": "apio-definitions"
18 | },
19 | "release": {
20 | "version": "2025.10.20",
21 | "release-tag": "${YYYY-MM-DD}",
22 | "package-file": "apio-definitions-${YYYYMMDD}.tgz"
23 | }
24 | },
25 | // -- Examples
26 | "examples": {
27 | "repository": {
28 | "organization": "fpgawars",
29 | "name": "apio-examples"
30 | },
31 | "release": {
32 | "version": "2025.12.01",
33 | "release-tag": "${YYYY-MM-DD}",
34 | "package-file": "apio-examples-${YYYYMMDD}.tgz"
35 | }
36 | },
37 | // -- OSS cad suite
38 | "oss-cad-suite": {
39 | "repository": {
40 | "organization": "fpgawars",
41 | "name": "tools-oss-cad-suite"
42 | },
43 | "release": {
44 | "version": "2025.09.24",
45 | "release-tag": "${YYYY-MM-DD}",
46 | "package-file": "apio-oss-cad-suite-${PLATFORM}-${YYYYMMDD}.tgz"
47 | }
48 | },
49 | // -- Graphviz
50 | "graphviz": {
51 | "repository": {
52 | "organization": "fpgawars",
53 | "name": "tools-graphviz"
54 | },
55 | "release": {
56 | "version": "2025.06.13",
57 | "release-tag": "${YYYY-MM-DD}",
58 | "package-file": "apio-graphviz-${PLATFORM}-${YYYYMMDD}.tgz"
59 | }
60 | },
61 | // -- Verible
62 | "verible": {
63 | "repository": {
64 | "organization": "fpgawars",
65 | "name": "tools-verible"
66 | },
67 | "release": {
68 | "version": "2025.06.13",
69 | "release-tag": "${YYYY-MM-DD}",
70 | "package-file": "apio-verible-${PLATFORM}-${YYYYMMDD}.tgz"
71 | }
72 | },
73 | // -- Drivers
74 | "drivers": {
75 | "repository": {
76 | "organization": "fpgawars",
77 | "name": "tools-drivers"
78 | },
79 | "release": {
80 | "version": "2025.06.13",
81 | "release-tag": "${YYYY-MM-DD}",
82 | "package-file": "apio-drivers-${PLATFORM}-${YYYYMMDD}.tgz"
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/docs/project-structure.md:
--------------------------------------------------------------------------------
1 | # Apio Project Structure
2 |
3 | ## Directory structure
4 |
5 | An Apio project consists of a directory that contains the required project file `apio.ini` and the project files. Below is an example of a minimal ICE40 project (`alhambra-ii/blinky` example) which contains the project file `apio.ini`, a Verilog source file `blinky.v`, and the pinout constraints file `pinout.pcf`, which maps symbolic pin names to pin numbers.
6 |
7 | ```
8 | my-project/
9 | ├── apio.ini
10 | ├── blinky.v
11 | └── pinout.pcf
12 | ```
13 |
14 | The next example (alhambra-ii/bcd-output) is more complex, with Verilog `*.v` source files, their `_tb.*` testbenches, and their `*.gtkw` GTKWave state files organized in a directory tree.
15 |
16 | ```
17 | my-project/
18 | ├── apio.ini
19 | ├── bcd
20 | │ ├── bcd_digit_tb.gtkw
21 | │ ├── bcd_digit_tb.v
22 | │ └── bcd_digit.v
23 | ├── main_tb.gtkw
24 | ├── main_tb.v
25 | ├── main.v
26 | ├── pinout.pcf
27 | ├── testing
28 | │ └── apio_testing.vh
29 | └── util
30 | ├── reset_gen.v
31 | ├── ticker_tb.gtkw
32 | ├── ticker_tb.v
33 | └── ticker.v
34 | ```
35 |
36 | **Directory structure rules**
37 |
38 | - The project file `apio.ini` and the pinout constraints file should reside in the top-level directory.
39 | - Source files and testbenches can reside in the root directory or in any subdirectory.
40 | - Testbenches' GTKWave state files (`.gtkw`) should reside in the same directory as their respective testbenches.
41 |
42 | ## Custom definitions
43 |
44 | The apio definitions files `boards.jsonc`, `fpga.jsonc`, and `programmers.jsonc` can be overridden
45 | by placing files with same names at the project's top directory. This allows debugging
46 | and testing custom boards that are not included yet in the Apio's `definitions` package.
47 |
48 | ## Output files
49 |
50 | Apio commands write their output to the directory `_build/` under the project root directory, where `` is the active environment name from `apio.ini`. For example, when building for an environment called `default`, the output directory is `_build/default`. The example below shows the results of the `apio build` command, including the ICE40 bitstream file `hardware.bin` and intermediate files created during the build.
51 |
52 | ```
53 | my-project/
54 | ├── _build
55 | │ └── default
56 | │ ├── hardware.asc
57 | │ ├── hardware.bin
58 | │ ├── hardware.json
59 | │ ├── hardware.pnr
60 | │ └── scons.params
61 | ├── apio.ini
62 | ├── blinky.v
63 | └── pinout.pcf
64 | ```
65 |
66 | > The command `apio clean` can be used to delete all Apio-generated files and force a build from scratch.
67 |
68 | ## Using Git
69 |
70 | When working with Git, we recommend including the following in your `.gitignore` file to avoid committing build artifacts and system files:
71 |
72 | **.gitignore**
73 |
74 | ```
75 | _build
76 | .DS_Store
77 | ```
78 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_preferences.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio preferences" command."""
2 |
3 | import re
4 | from tests.conftest import ApioRunner
5 | from apio.common.apio_console import cunstyle
6 | from apio.commands.apio import apio_top_cli as apio
7 |
8 |
9 | def test_colors_on_off(apio_runner: ApioRunner):
10 | """Test "apio preferences" with different parameters"""
11 |
12 | with apio_runner.in_sandbox() as sb:
13 |
14 | # -- Execute "apio preferences"
15 | result = sb.invoke_apio_cmd(apio, ["preferences"])
16 | sb.assert_result_ok(result)
17 | assert "'apio preferences' allows" in cunstyle(result.output)
18 | assert "-t, --theme [light|dark|no-colors]" in result.output
19 | assert result.output != cunstyle(result.output) # Colored.
20 |
21 | # -- Execute "apio preferences --theme dark"
22 | result = sb.invoke_apio_cmd(apio, ["preferences", "--theme", "dark"])
23 | sb.assert_result_ok(result)
24 | assert "Theme set to [dark]" in result.output
25 | assert result.output != cunstyle(result.output) # Colored.
26 |
27 | # -- Execute "apio preferences --list". It should reports the dark
28 | # -- theme.
29 | result = sb.invoke_apio_cmd(apio, ["preferences", "--list"])
30 | sb.assert_result_ok(result)
31 | assert result.output != cunstyle(result.output) # Colored.
32 | assert "dark" in result.output
33 |
34 | # -- Execute "apio system info". It should emit colors.
35 | result = sb.invoke_apio_cmd(apio, ["info", "system"])
36 | sb.assert_result_ok(result)
37 | assert result.output != cunstyle(result.output) # Colored.
38 |
39 | # -- Execute "apio preferences --theme no-colors"
40 | result = sb.invoke_apio_cmd(
41 | apio, ["preferences", "--theme", "no-colors"]
42 | )
43 | sb.assert_result_ok(result)
44 | assert "Theme set to [no-colors]" in result.output
45 |
46 | # -- Execute "apio preferences --list". It should reports the
47 | # -- no-colors theme.
48 | result = sb.invoke_apio_cmd(apio, ["preferences", "--list"])
49 | sb.assert_result_ok(result)
50 | assert re.search(r"Theme.*no-colors", result.output), result.output
51 | assert result.output == cunstyle(result.output) # Non colored..
52 |
53 | # -- Execute "apio preferences --colors".
54 | result = sb.invoke_apio_cmd(apio, ["preferences", "--colors"])
55 | # -- It's normal to have 'error' in the output test.
56 | sb.assert_result_ok(result, bad_words=[])
57 | assert result.output != cunstyle(result.output) # Colored..
58 | # -- Ideally we would like to have this assertion enabled but
59 | # -- when running on github workflows we sometimes get different
60 | # -- colors.
61 | # assert "\x1b[38;5;237mbar.back " in result.output
62 |
--------------------------------------------------------------------------------
/docs/migrating-from-apio-0.9.5.md:
--------------------------------------------------------------------------------
1 | # Migrating from Apio 0.9.5
2 |
3 | Apio 1.x.x introduces many improvements compared to Apio 0.9.5. Many of the changes were done in a backward compatible way, but some do require user attention. On this page, we outline the main changes from a compatibility point of view to help users migrate their projects successfully to Apio 1.x.x.
4 |
5 | ## Uninstall Apio 0.9.5
6 |
7 | It is recommended to first delete Apio 0.9.5 before installing Apio 1.x.x. The steps to do so are:
8 |
9 | 1. Delete the Apio Python package `pip uninstall apio`
10 |
11 | 2. Delete the directory `.apio` under the user home directory. That directory contains packages and other transient files used by Apio.
12 |
13 | ## Create project file `apio.ini`
14 |
15 | Apio 1.x.x requires a project file called `apio.ini` in the directory of each Apio project. Make sure your project has a text file called `apio.ini` with the content below, replace __ with the id of your board (e.g. `alhambra-ii`) and replace __ with the name of the top Verilog module of your project (e.g. `Blinky`).
16 |
17 | ```
18 | [env:default]
19 | board =
20 | main-module =
21 | ```
22 |
23 | ## Delete calls to the verilog function `$dumpfile()`.
24 |
25 | Remove from your testbenches all calls to the Verilog function `$dumpfile()`. The location of the generated simulation files is now automatically controlled by Apio.
26 |
27 | ## Know the new commands
28 |
29 | The hierarchy and names of some Apio commands were changed in Apio 1.x.x, and the table below will help you migrate from the old to the new commands. You can also use the `-h` option for detailed information on any command level, for example `apio -h`, `apio devices -h`, and `apio devices usb -h`.
30 |
31 | | Apio 0.9.5 | Apio 1.x.x | Comments |
32 | | :--------------------------- | :-------------------------- | :--------------------------- |
33 | | `apio boards --fpga` | `apio fpgas` | List supported FPGAs |
34 | | `apio boards --list` | `apio boards` | List supported boards |
35 | | `apio drivers --ftdi-enable` | `apio drivers install ftdi` | Install FTDI driver |
36 | | `apio examples --files` | `apio examples fetch` | Fetch an example |
37 | | `apio examples --list` | `apio examples list` | List examples |
38 | | `apio init` | `apio create` | Create an apio.ini file |
39 | | `apio install --all` | `apio packages update` | Install Apio packages |
40 | | `apio install --list` | `apio packages list` | List installed apio packages |
41 | | `apio system --lsftdi` | `apio devices usb` | List FTDI and USB devices |
42 | | `apio system --lsserial` | `apio devices serial` | List serial ports |
43 | | `apio time` | `apio report` | Report design timing. |
44 | | `apio verify` | `apio lint` | Verify the source code. |
45 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_create.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio create" command."""
2 |
3 | from pathlib import Path
4 | from os.path import isfile, exists
5 | from typing import Dict
6 | from configobj import ConfigObj
7 | from tests.conftest import ApioRunner
8 | from apio.commands.apio import apio_top_cli as apio
9 |
10 |
11 | def _check_ini_file(apio_ini: Path, expected_vars: Dict[str, str]) -> None:
12 | """Assert that apio.ini contains exactly the given vars."""
13 | # Read the ini file.
14 | assert isfile(apio_ini)
15 | conf = ConfigObj(str(apio_ini))
16 | # Check the expected comment at the top.
17 | assert "# APIO project configuration file" in conf.initial_comment[0]
18 | # Check the expected vars.
19 | assert conf.dict() == {"env:default": expected_vars}
20 |
21 |
22 | def test_create(apio_runner: ApioRunner):
23 | """Test "apio create" with different parameters"""
24 |
25 | with apio_runner.in_sandbox() as sb:
26 |
27 | apio_ini = Path("apio.ini")
28 | assert not exists(apio_ini)
29 |
30 | # -- Execute "apio create"
31 | result = sb.invoke_apio_cmd(apio, ["create"])
32 | assert result.exit_code != 0, result.output
33 | assert "Error: Missing option" in result.output
34 | assert not exists(apio_ini)
35 |
36 | # -- Execute "apio create --board no-such-board"
37 | result = sb.invoke_apio_cmd(
38 | apio, ["create", "--board", "no-such-board"]
39 | )
40 | assert result.exit_code == 1, result.output
41 | assert "Error: Unknown board id 'no-such-board'" in result.output
42 | assert not exists(apio_ini)
43 |
44 | # -- Execute "apio create --board alhambra-ii"
45 | result = sb.invoke_apio_cmd(apio, ["create", "--board", "alhambra-ii"])
46 | sb.assert_result_ok(result)
47 | assert "was created successfully." in result.output
48 | _check_ini_file(
49 | apio_ini, {"board": "alhambra-ii", "top-module": "main"}
50 | )
51 |
52 | # -- Execute "apio create --board alhambra-ii
53 | # -- --top-module my_module" with 'y' input"
54 | result = sb.invoke_apio_cmd(
55 | apio,
56 | [
57 | "create",
58 | "--board",
59 | "alhambra-ii",
60 | "--top-module",
61 | "my_module",
62 | ],
63 | )
64 | assert result.exit_code != 0
65 | assert "Error: The file apio.ini already exists." in result.output
66 | _check_ini_file(
67 | apio_ini, {"board": "alhambra-ii", "top-module": "main"}
68 | )
69 |
70 | # -- Execute "apio create --board alhambra-ii -p aa/bb"
71 | result = sb.invoke_apio_cmd(
72 | apio, ["create", "--board", "alhambra-ii", "-p", "aa/bb"]
73 | )
74 | sb.assert_result_ok(result)
75 | assert "was created successfully." in result.output
76 | _check_ini_file(
77 | Path("aa/bb") / apio_ini,
78 | {"board": "alhambra-ii", "top-module": "main"},
79 | )
80 |
--------------------------------------------------------------------------------
/apio/commands/apio_build.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # -- This file is part of the Apio project
3 | # -- (C) 2016-2024 FPGAwars
4 | # -- Authors
5 | # -- * Jesús Arroyo (2016-2019)
6 | # -- * Juan Gonzalez (obijuan) (2019-2024)
7 | # -- License GPLv2
8 | """Implementation of 'apio build' command"""
9 |
10 | import sys
11 | from typing import Optional
12 | from pathlib import Path
13 | import click
14 | from apio.utils import cmd_util
15 | from apio.managers.scons_manager import SConsManager
16 | from apio.commands import options
17 | from apio.common.proto.apio_pb2 import Verbosity
18 | from apio.apio_context import (
19 | ApioContext,
20 | PackagesPolicy,
21 | ProjectPolicy,
22 | RemoteConfigPolicy,
23 | )
24 |
25 | # ------------ apio build
26 |
27 | # -- Text in the rich-text format of the python rich library.
28 | APIO_BUILD_HELP = """
29 | The command 'apio build' processes the project’s synthesis source files and \
30 | generates a bitstream file, which can then be uploaded to your FPGA.
31 |
32 | Examples:[code]
33 | apio build # Typical usage
34 | apio build -e debug # Set the apio.ini env.
35 | apio build -v # Verbose info (all)
36 | apio build --verbose-synth # Verbose synthesis info
37 | apio build --verbose-pnr # Verbose place and route info[/code]
38 |
39 | NOTES:
40 | * The files are sorted in a deterministic lexicographic order.
41 | * You can specify the name of the top module in apio.ini.
42 | * The build command ignores testbench files (*_tb.v, and *_tb.sv).
43 | * It is unnecessary to run 'apio build' before 'apio upload'.
44 | * To force a rebuild from scratch use the command 'apio clean' first.
45 | """
46 |
47 |
48 | @click.command(
49 | name="build",
50 | cls=cmd_util.ApioCommand,
51 | short_help="Synthesize the bitstream.",
52 | help=APIO_BUILD_HELP,
53 | )
54 | @click.pass_context
55 | @options.env_option_gen()
56 | @options.project_dir_option
57 | @options.verbose_option
58 | @options.verbose_synth_option
59 | @options.verbose_pnr_option
60 | def cli(
61 | _: click.Context,
62 | # Options
63 | env: Optional[str],
64 | project_dir: Optional[Path],
65 | verbose: bool,
66 | verbose_synth: bool,
67 | verbose_pnr: bool,
68 | ):
69 | """Implements the apio build command. It invokes the toolchain
70 | to synthesize the source files into a bitstream file.
71 | """
72 |
73 | # -- Create the apio context.
74 | apio_ctx = ApioContext(
75 | project_policy=ProjectPolicy.PROJECT_REQUIRED,
76 | remote_config_policy=RemoteConfigPolicy.CACHED_OK,
77 | packages_policy=PackagesPolicy.ENSURE_PACKAGES,
78 | project_dir_arg=project_dir,
79 | env_arg=env,
80 | )
81 |
82 | # -- Create the scons manager.
83 | scons = SConsManager(apio_ctx)
84 |
85 | # -- Build the project with the given parameters
86 | exit_code = scons.build(
87 | Verbosity(all=verbose, synth=verbose_synth, pnr=verbose_pnr)
88 | )
89 |
90 | # -- Done!
91 | sys.exit(exit_code)
92 |
93 |
94 | # Advanced notes: https://github.com/FPGAwars/apio/wiki/Commands#apio-build
95 |
--------------------------------------------------------------------------------
/tests/unit_tests/utils/test_usb_util.py:
--------------------------------------------------------------------------------
1 | """Tests of usb_util.py"""
2 |
3 | from typing import List
4 | from apio.utils.usb_util import (
5 | UsbDevice,
6 | UsbDeviceFilter,
7 | )
8 |
9 |
10 | def test_device_summaries():
11 | """Test usb device summary() string."""
12 | device = UsbDevice("0403", "6010", 0, 1, "m0", "p0", "sn0", "t0")
13 | assert device.summary() == "[0403:6010] [0:1] [m0] [p0] [sn0]"
14 |
15 |
16 | def test_filtering():
17 | """Test the filtering function."""
18 | devs: List[UsbDevice] = [
19 | UsbDevice("0403", "6010", 0, 1, "m0", "p0", "sn0", "t0"), # devs[0]
20 | UsbDevice("0403", "6020", 3, 1, "m1", "p1", "sn1", "t1"), # devs[1]
21 | UsbDevice("0405", "6020", 3, 1, "m2", "p2", "sn2", "t2"), # devs[2]
22 | UsbDevice("0403", "6020", 2, 1, "m3", "p3", "sn3", "t3"), # devs[3]
23 | UsbDevice("0403", "6010", 1, 1, "m4", "p4", "sn4", "t4"), # devs[4]
24 | UsbDevice("0405", "6010", 1, 1, "m5", "p5", "sn5", "t5"), # devs[5]
25 | ]
26 |
27 | # -- All filtering disabled.
28 | filt = UsbDeviceFilter()
29 | assert filt.summary() == "[all]"
30 | assert filt.filter(devs) == devs
31 |
32 | # -- Filter by VID
33 | filt = UsbDeviceFilter().set_vendor_id("9999")
34 | assert filt.summary() == "[VID=9999]"
35 | assert filt.filter(devs) == []
36 |
37 | filt = UsbDeviceFilter().set_vendor_id("0405")
38 | assert filt.summary() == "[VID=0405]"
39 | assert filt.filter(devs) == [devs[2], devs[5]]
40 |
41 | # -- Filter by PID
42 | filt = UsbDeviceFilter().set_product_id("9999")
43 | assert filt.summary() == "[PID=9999]"
44 | assert filt.filter(devs) == []
45 |
46 | filt = UsbDeviceFilter().set_product_id("6020")
47 | assert filt.summary() == "[PID=6020]"
48 | assert filt.filter(devs) == [devs[1], devs[2], devs[3]]
49 |
50 | # -- Filter by description regex
51 | filt = UsbDeviceFilter().set_product_regex("no-such-device")
52 | assert filt.summary() == '[REGEX="no-such-device"]'
53 | assert filt.filter(devs) == []
54 |
55 | filt = UsbDeviceFilter().set_product_regex("^p2$")
56 | assert filt.summary() == '[REGEX="^p2$"]'
57 | assert filt.filter(devs) == [devs[2]]
58 |
59 | filt = UsbDeviceFilter().set_product_regex("p2")
60 | assert filt.summary() == '[REGEX="p2"]'
61 | assert filt.filter(devs) == [devs[2]]
62 |
63 | filt = UsbDeviceFilter().set_product_regex("(p3)|(p2)")
64 | assert filt.summary() == '[REGEX="(p3)|(p2)"]'
65 | assert filt.filter(devs) == [devs[2], devs[3]]
66 |
67 | # -- Filter by serial number
68 | filt = UsbDeviceFilter().set_serial_num("no-such-device")
69 | assert filt.summary() == '[S/N="no-such-device"]'
70 | assert filt.filter(devs) == []
71 |
72 | filt = UsbDeviceFilter().set_serial_num("sn2")
73 | assert filt.summary() == '[S/N="sn2"]'
74 | assert filt.filter(devs) == [devs[2]]
75 |
76 | # -- Filter by VID, PID
77 | filt = UsbDeviceFilter().set_vendor_id("0403").set_product_id("6010")
78 | assert filt.summary() == "[VID=0403, PID=6010]"
79 | assert filt.filter(devs) == [devs[0], devs[4]]
80 |
--------------------------------------------------------------------------------
/apio/commands/apio_test.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # -- This file is part of the Apio project
3 | # -- (C) 2016-2024 FPGAwars
4 | # -- Authors
5 | # -- * Jesús Arroyo (2016-2019)
6 | # -- * Juan Gonzalez (obijuan) (2019-2024)
7 | # -- License GPLv2
8 | """Implementation of 'apio test' command"""
9 |
10 | import sys
11 | from typing import Optional
12 | from pathlib import Path
13 | import click
14 | from apio.managers.scons_manager import SConsManager
15 | from apio.commands import options
16 | from apio.apio_context import (
17 | ApioContext,
18 | PackagesPolicy,
19 | ProjectPolicy,
20 | RemoteConfigPolicy,
21 | )
22 | from apio.common.proto.apio_pb2 import ApioTestParams
23 | from apio.utils import cmd_util
24 |
25 |
26 | # --------- apio test
27 |
28 |
29 | # -- Text in the rich-text format of the python rich library.
30 | APIO_TEST_HELP = """
31 | The command 'apio test' simulates one or all the testbenches in the project \
32 | and is useful for automated testing of your design. Testbenches are expected \
33 | to have names ending with _tb (e.g., my_module_tb.v) and should exit with the \
34 | '$fatal' directive if an error is detected.
35 |
36 | Examples:[code]
37 | apio test # Run all *_tb.v testbenches.
38 | apio test my_module_tb.v # Run a single testbench.[/code]
39 |
40 | [NOTE] Testbench specification is always the testbench file path relative to \
41 | the project directory, even if using the '--project-dir' option.
42 |
43 | [IMPORTANT] Avoid using the Verilog '$dumpfile()' function in your \
44 | testbenches, as this may override the default name and location Apio sets \
45 | for the generated .vcd file.
46 |
47 | For a sample testbench compatible with Apio features, see: \
48 | https://github.com/FPGAwars/apio-examples/tree/master/upduino31/testbench
49 |
50 | [b][Hint][/b] To simulate a testbench with a graphical visualization \
51 | of the signals, refer to the 'apio sim' command.
52 | """
53 |
54 |
55 | @click.command(
56 | name="test",
57 | cls=cmd_util.ApioCommand,
58 | short_help="Test all or a single verilog testbench module.",
59 | help=APIO_TEST_HELP,
60 | )
61 | @click.pass_context
62 | @click.argument("testbench_file", nargs=1, required=False)
63 | @options.env_option_gen()
64 | @options.project_dir_option
65 | # @options.testbench
66 | def cli(
67 | _: click.Context,
68 | # Arguments
69 | testbench_file: str,
70 | # Options
71 | env: Optional[str],
72 | project_dir: Optional[Path],
73 | ):
74 | """Implements the test command."""
75 |
76 | # -- Create the apio context.
77 | apio_ctx = ApioContext(
78 | project_policy=ProjectPolicy.PROJECT_REQUIRED,
79 | remote_config_policy=RemoteConfigPolicy.CACHED_OK,
80 | packages_policy=PackagesPolicy.ENSURE_PACKAGES,
81 | project_dir_arg=project_dir,
82 | env_arg=env,
83 | )
84 |
85 | # -- Create the scons manager.
86 | scons = SConsManager(apio_ctx)
87 |
88 | # -- Construct the test params
89 | test_params = ApioTestParams(
90 | testbench=testbench_file if testbench_file else None
91 | )
92 |
93 | exit_code = scons.test(test_params)
94 | sys.exit(exit_code)
95 |
--------------------------------------------------------------------------------
/apio/commands/apio_create.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # -- This file is part of the Apio project
3 | # -- (C) 2016-2024 FPGAwars
4 | # -- Authors
5 | # -- * Jesús Arroyo (2016-2019)
6 | # -- * Juan Gonzalez (obijuan) (2019-2024)
7 | # -- License GPLv2
8 | """Implementation of 'apio create' command"""
9 |
10 | import sys
11 | from typing import Optional
12 | from pathlib import Path
13 | import click
14 | from apio.common.apio_console import cerror
15 | from apio.utils import util, cmd_util
16 | from apio.commands import options
17 | from apio.apio_context import (
18 | ApioContext,
19 | PackagesPolicy,
20 | ProjectPolicy,
21 | RemoteConfigPolicy,
22 | )
23 | from apio.managers.project import (
24 | DEFAULT_TOP_MODULE,
25 | create_project_file,
26 | )
27 |
28 |
29 | board_option = click.option(
30 | "board", # Var name.
31 | "-b",
32 | "--board",
33 | type=str,
34 | required=True,
35 | metavar="BOARD",
36 | help="Set the board.",
37 | cls=cmd_util.ApioOption,
38 | )
39 |
40 | # -------------- apio create
41 |
42 | # -- Text in the rich-text format of the python rich library.
43 | APIO_CREATE_HELP = """
44 | The command 'apio create' creates a new 'apio.ini' project file and is \
45 | typically used when setting up a new Apio project.
46 |
47 | Examples:[code]
48 | apio create --board alhambra-ii
49 | apio create --board alhambra-ii --top-module MyModule[/code]
50 |
51 | [b][NOTE][/b] This command only creates a new 'apio.ini' file, rather than a \
52 | complete and buildable project. To create complete projects, refer to the \
53 | 'apio examples' command.
54 | """
55 |
56 |
57 | @click.command(
58 | name="create",
59 | cls=cmd_util.ApioCommand,
60 | short_help="Create an apio.ini project file.",
61 | help=APIO_CREATE_HELP,
62 | )
63 | @click.pass_context
64 | @board_option
65 | @options.top_module_option_gen(short_help="Set the top level module name.")
66 | @options.project_dir_option
67 | def cli(
68 | _: click.Context,
69 | # Options
70 | board: str,
71 | top_module: str,
72 | project_dir: Optional[Path],
73 | ):
74 | """Create a project file."""
75 |
76 | # Board is annotated above as required so must exist.
77 | assert board is not None
78 |
79 | if not top_module:
80 | top_module = DEFAULT_TOP_MODULE
81 |
82 | # -- Create the apio context.
83 | apio_ctx = ApioContext(
84 | project_policy=ProjectPolicy.NO_PROJECT,
85 | remote_config_policy=RemoteConfigPolicy.CACHED_OK,
86 | packages_policy=PackagesPolicy.ENSURE_PACKAGES,
87 | )
88 |
89 | # -- Make sure the board exist.
90 | if board not in apio_ctx.boards:
91 | cerror(f"Unknown board id '{board}'.")
92 | sys.exit(1)
93 |
94 | # -- Determine the new project directory. Create if needed.
95 | project_dir: Path = util.user_directory_or_cwd(
96 | project_dir, description="Project", create_if_missing=True
97 | )
98 |
99 | # Create the apio.ini file. It exists with an error status if any error.
100 | create_project_file(
101 | project_dir,
102 | board,
103 | top_module,
104 | )
105 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | # The project name.
2 | site_name: Apio 1.x.x Documentation
3 |
4 | # The temporary directory with the generated site.
5 | site_dir: _site
6 |
7 | plugins:
8 | - search
9 |
10 | extra_css:
11 | - stylesheets/extra.css
12 |
13 | theme:
14 | name: material
15 |
16 | # This controls the behavior of the navigation.
17 | features:
18 | - navigation.sections # Enables second-level grouping
19 | - navigation.expand # Expands all sections and subsections by default
20 | - navigation.tabs
21 | - content.code.copy # 'copy' button in code blocks
22 |
23 | markdown_extensions:
24 | - admonition
25 | - pymdownx.extra
26 | - pymdownx.tabbed:
27 | alternate_style: true
28 | - pymdownx.superfences
29 | - toc:
30 | permalink: true
31 |
32 | # Navigation tree
33 | nav:
34 | - Home: index.md
35 | - Getting Started:
36 | - Quick start: quick-start.md
37 | - System requirements: system-requirements.md
38 | - Installing Apio IDE: installing-apio-ide.md
39 | - Installing Apio CLI: installing-apio-cli.md
40 | - Video tutorial: video-tutorial.md
41 | - Migrating from Apio 0.9.5: migrating-from-apio-0.9.5.md
42 | - Apio Commands:
43 | - Commands list: commands-list.md
44 | - api: cmd-apio-api.md
45 | - boards: cmd-apio-boards.md
46 | - build: cmd-apio-build.md
47 | - clean: cmd-apio-clean.md
48 | - create: cmd-apio-create.md
49 | - devices: cmd-apio-devices.md
50 | - docs: cmd-apio-docs.md
51 | - drivers: cmd-apio-drivers.md
52 | - examples: cmd-apio-examples.md
53 | - format: cmd-apio-format.md
54 | - fpgas: cmd-apio-fpgas.md
55 | - graph: cmd-apio-graph.md
56 | - info: cmd-apio-info.md
57 | - lint: cmd-apio-lint.md
58 | - packages: cmd-apio-packages.md
59 | - preferences: cmd-apio-preferences.md
60 | - raw: cmd-apio-raw.md
61 | - report: cmd-apio-report.md
62 | - sim: cmd-apio-sim.md
63 | - test: cmd-apio-test.md
64 | - upgrade: cmd-apio-upgrade.md
65 | - upload: cmd-apio-upload.md
66 | - Apio Projects:
67 | - Project structure: project-structure.md
68 | - Project file apio.ini: project-file.md
69 | - FPGA Boards:
70 | - Supported boards: supported-boards.md
71 | - Supported FPGAs: supported-fpgas.md
72 | - Custom boards: custom-boards.md
73 | - Contributing definitions: contributing-definitions.md
74 | - Board drivers: board-drivers.md
75 | - Apio Examples:
76 | - Apio examples: apio-examples.md
77 | - Using examples: using-examples.md
78 | - Contributing examples: contributing-examples.md
79 | - Apio Tips:
80 | - Apio command line: command-line.md
81 | - Using System Verilog: using-system-verilog.md
82 | - Using testbenches: using-testbenches.md
83 | - Raw tools: raw-tools.md
84 | - Apio Development:
85 | - Apio repositories: apio-repositories.md
86 | - Development environment: development-environment.md
87 | - Apio's terminology: terminology.md
88 | - Command execution: command-execution.md
89 | - Updating the docs: updating-the-docs.md
90 | - Creating an Apio version: creating-apio-version.md
91 | - Creating a package version: creating-package-version.md
92 | - Help: help.md
93 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_info.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio system" command."""
2 |
3 | import re
4 | from tests.conftest import ApioRunner
5 | from apio.commands.apio import apio_top_cli as apio
6 | from apio.common.apio_console import cunstyle, cwidth
7 |
8 |
9 | def test_apio_info(apio_runner: ApioRunner):
10 | """Test "apio info" with different parameters"""
11 |
12 | with apio_runner.in_sandbox() as sb:
13 | # -- For debugging table column truncation in github testing.
14 | print(f"Apio console width = {cwidth()}")
15 |
16 | # -- Execute "apio info"
17 | result = sb.invoke_apio_cmd(apio, ["info"])
18 | sb.assert_result_ok(result)
19 | assert "platforms" in result.output
20 |
21 | # -- Execute "apio info system"
22 | result = sb.invoke_apio_cmd(apio, ["info", "system"])
23 | sb.assert_result_ok(result)
24 | assert "Platform id" in result.output
25 | # -- The these env options are set by the apio text fixture. We
26 | # -- relax the expression to allow additional env vars that are
27 | # -- injected to the test such as APIO_REMOTE_URL_CONFIG
28 | assert re.search(
29 | r"Active env options \[[^]]*APIO_HOME[^]]*\]", result.output
30 | )
31 |
32 | # -- Execute "apio info platforms"
33 | result = sb.invoke_apio_cmd(apio, ["info", "platforms"])
34 | sb.assert_result_ok(result)
35 | assert "darwin-arm64" in result.output
36 | assert "Mac OSX" in result.output
37 | assert "ARM 64 bit (Apple Silicon)" in result.output
38 |
39 | # -- Execute "apio info commands"
40 | result = sb.invoke_apio_cmd(apio, ["info", "commands"])
41 | sb.assert_result_ok(result)
42 | assert " build " in cunstyle(result.output)
43 | assert "Synthesize the bitstream." in result.output
44 | assert "[build](cmd-apio-build.md)" not in result.output
45 |
46 | # -- Execute "apio info commands --docs"
47 | result = sb.invoke_apio_cmd(apio, ["info", "commands", "--docs"])
48 | sb.assert_result_ok(result)
49 | assert "[build](cmd-apio-build.md)" in result.output
50 | assert "Synthesize the bitstream." in result.output
51 | assert " build " not in cunstyle(result.output)
52 |
53 | # -- Execute "apio info colors"
54 | result = sb.invoke_apio_cmd(apio, ["info", "colors"])
55 | sb.assert_result_ok(result)
56 | assert result.output != cunstyle(result.output) # Colored
57 | assert "ANSI Colors" in result.output
58 | assert "\x1b[31m 1 red \x1b[0m" in result.output
59 |
60 | # -- Execute "apio info themes"
61 | result = sb.invoke_apio_cmd(apio, ["info", "themes"])
62 | # -- It's normal to have 'error' in the output text.
63 | sb.assert_result_ok(result, bad_words=[])
64 | assert result.output != cunstyle(result.output) # Colored
65 | assert "NO-COLORS" in result.output
66 | assert "apio.cmd_name\x1b[0m" in result.output
67 |
68 | # -- Execute "apio info system". It should not emit colors.
69 | result = sb.invoke_apio_cmd(apio, ["info", "system"])
70 | sb.assert_result_ok(result)
71 | assert result.output != cunstyle(result.output) # Colored
72 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_examples.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio examples" command."""
2 |
3 | import os
4 | from os.path import getsize
5 | from tests.conftest import ApioRunner
6 | from apio.commands.apio import apio_top_cli as apio
7 | from apio.common.apio_console import cunstyle
8 |
9 |
10 | def test_examples(apio_runner: ApioRunner):
11 | """Tests the listing and fetching apio examples."""
12 |
13 | with apio_runner.in_sandbox() as sb:
14 |
15 | # -- Execute "apio examples"
16 | result = sb.invoke_apio_cmd(apio, ["examples"])
17 | sb.assert_result_ok(result)
18 | assert "Subcommands:" in cunstyle(result.output)
19 | assert "examples list" in cunstyle(result.output)
20 |
21 | # -- 'apio examples list'
22 | result = sb.invoke_apio_cmd(apio, ["examples", "list"])
23 | sb.assert_result_ok(result)
24 | assert "alhambra-ii/ledon" in result.output
25 | assert "Turning on a led" in result.output
26 |
27 | # -- 'apio examples list --docs'
28 | result = sb.invoke_apio_cmd(apio, ["examples", "list", "--docs"])
29 | sb.assert_result_ok(result)
30 | assert "alhambra-ii/ledon" in result.output
31 | assert "Turning on a led" in result.output
32 |
33 | # -- 'apio examples fetch alhambra-ii/ledon' (colors off)
34 | result = sb.invoke_apio_cmd(
35 | apio,
36 | ["examples", "fetch", "alhambra-ii/ledon"],
37 | terminal_mode=False,
38 | )
39 | sb.assert_result_ok(result)
40 | assert "Copying alhambra-ii/ledon example files" in result.output
41 | assert (
42 | "Example 'alhambra-ii/ledon' fetched successfully" in result.output
43 | )
44 | assert getsize("apio.ini")
45 | assert getsize("ledon.v")
46 |
47 | # -- 'apio examples fetch alhambra-ii' (colors off)
48 | os.makedirs("temp", exist_ok=False)
49 | os.chdir("temp")
50 | result = sb.invoke_apio_cmd(
51 | apio,
52 | ["examples", "fetch", "alhambra-ii"],
53 | terminal_mode=False,
54 | )
55 | sb.assert_result_ok(result)
56 | assert "Fetching alhambra-ii/blinky" in result.output
57 | assert "Fetching alhambra-ii/ledon" in result.output
58 | assert "Examples fetched successfully" in result.output
59 | assert getsize("ledon/ledon.v")
60 | os.chdir("..")
61 |
62 | # -- 'apio examples fetch alhambra-ii/ledon -d dir1' (colors off)
63 | result = sb.invoke_apio_cmd(
64 | apio,
65 | ["examples", "fetch", "alhambra-ii/ledon", "-d", "dir1"],
66 | terminal_mode=False,
67 | )
68 | sb.assert_result_ok(result)
69 | assert "Copying alhambra-ii/ledon example files" in result.output
70 | assert (
71 | "Example 'alhambra-ii/ledon' fetched successfully" in result.output
72 | )
73 | assert getsize("dir1/ledon.v")
74 |
75 | # -- 'apio examples fetch alhambra -d dir2 (colors off)
76 | result = sb.invoke_apio_cmd(
77 | apio,
78 | ["examples", "fetch", "alhambra-ii", "-d", "dir2"],
79 | terminal_mode=False,
80 | )
81 | sb.assert_result_ok(result)
82 | assert "Examples fetched successfully" in result.output
83 | assert getsize("dir2/ledon/ledon.v")
84 |
--------------------------------------------------------------------------------
/tests/unit_tests/commands/test_apio_fpgas.py:
--------------------------------------------------------------------------------
1 | """Test for the "apio boards" command."""
2 |
3 | from tests.conftest import ApioRunner
4 | from apio.commands.apio import apio_top_cli as apio
5 |
6 | CUSTOM_FPGAS = """
7 | {
8 | "custom-ice40hx4k-bg121": {
9 | "part-num": "CUSTOM-ICE40HX4K-BG121",
10 | "arch": "ice40",
11 | "size": "4k",
12 | "type": "hx4k",
13 | "pack": "bg121"
14 | },
15 | "ice40hx4k-tq144-8k": {
16 | "part-num": "MODIFIED-ICE40HX4K-TQ144",
17 | "arch": "ice40",
18 | "size": "8k",
19 | "type": "hx8k",
20 | "pack": "tq144:4k"
21 | }
22 | }
23 | """
24 |
25 |
26 | def test_fpgas_ok(apio_runner: ApioRunner):
27 | """Test "apio fpgas" command with standard fpgas.jsonc."""
28 |
29 | with apio_runner.in_sandbox() as sb:
30 |
31 | # -- Execute "apio fpgas"
32 | result = sb.invoke_apio_cmd(apio, ["fpgas"])
33 | sb.assert_result_ok(result)
34 | # -- Note: pytest sees the piped version of the command's output.
35 | # -- Run 'apio fpgas' | cat' to reproduce it.
36 | assert "Loading custom 'fpgas.jsonc'" not in result.output
37 | assert "ice40hx4k-tq144-8k" in result.output
38 | assert "my_custom_fpga" not in result.output
39 | assert "─────┐" in result.output # Graphic table border
40 | assert ":---" not in result.output # Graphic table border
41 |
42 | # -- Execute "apio fpgas --docs"
43 | result = sb.invoke_apio_cmd(apio, ["fpgas", "--docs"])
44 | sb.assert_result_ok(result)
45 | assert "Loading custom 'fpgas.jsonc'" not in result.output
46 | assert "ice40hx4k-tq144-8k" in result.output
47 | assert "my_custom_fpga" not in result.output
48 | assert "─────┐" not in result.output # Graphic table border
49 | assert ":---" in result.output # Graphic table border
50 |
51 |
52 | def test_custom_fpga(apio_runner: ApioRunner):
53 | """Test "apio fpgas" command with a custom fpgas.jsonc."""
54 |
55 | with apio_runner.in_sandbox() as sb:
56 |
57 | # -- Write apio.ini for apio to pick the project's default
58 | # -- fpgas.jsonc.
59 | sb.write_default_apio_ini()
60 |
61 | # -- Write a custom boards.jsonc file in the project's directory.
62 | sb.write_file("fpgas.jsonc", CUSTOM_FPGAS)
63 |
64 | # -- Execute "apio fpgas". It should include the customization.
65 | result = sb.invoke_apio_cmd(apio, ["fpgas"])
66 | sb.assert_result_ok(result)
67 | # -- Note: pytest sees the piped version of the command's output.
68 | # -- Run 'apio build' | cat' to reproduce it.
69 | assert "Loading custom 'fpgas.jsonc'" in result.output
70 | assert "gw1nz-lv1qn48c6-i5" in result.output
71 | assert "custom-ice40hx4k-bg121" in result.output
72 | assert "ice40hx4k-tq144-8k" in result.output
73 | assert "CUSTOM-ICE40HX4K-BG121" in result.output
74 |
75 | # -- Execute "apio fpgas --docs". It should not include the
76 | # -- customization.
77 | result = sb.invoke_apio_cmd(apio, ["fpgas", "--docs"])
78 | sb.assert_result_ok(result)
79 | # -- Note: pytest sees the piped version of the command's output.
80 | # -- Run 'apio build' | cat' to reproduce it.
81 | assert "Loading custom 'fpgas.jsonc'" not in result.output
82 | assert "gw1nz-lv1qn48c6-i5" in result.output
83 | assert "custom-ice40hx4k-bg121" not in result.output
84 | assert "ice40hx4k-tq144-8k" in result.output
85 | assert "CUSTOM-ICE40HX4K-BG121" not in result.output
86 |
--------------------------------------------------------------------------------
/docs/cmd-apio-info.md:
--------------------------------------------------------------------------------
1 | # Apio info
2 |
3 | ---
4 |
5 | ## apio info
6 |
7 | The `apio info` command group displays additional information about Apio and your system.
8 |
9 |
Options
10 |
11 | ` -h, --help Show this message and exit.`
12 |
13 |
Subcommands
14 |
15 | ```
16 | apio info platforms
17 | apio info system
18 | apio info colors
19 | apio info themes
20 | apio info commands
21 | ```
22 |
23 | ---
24 |
25 | ## apio info platforms
26 |
27 | The command `apio info platforms` lists the platform IDs supported by Apio and highlights your system's effective ID.
28 |
29 | > [ADVANCED] The automatic platform ID detection of Apio can be overridden by defining a different platform ID using the `APIO_PLATFORM` environment variable, though this is generally not recommended.
30 |
31 |
38 |
39 | ```
40 | -h, --help Show this message and exit
41 | ```
42 |
43 | ---
44 |
45 | ## apio info system
46 |
47 | The `apio info system` command displays general information about your system and Apio installation. Useful for diagnosing setup or environment issues.
48 |
49 |
50 | > [NOTE] For programmatic access to this information use `apio api get-system`.
51 |
52 |
53 | > [ADVANCED] The default location of the Apio home directory, where it saves preferences and packages, is `.apio` under your home directory. This can be changed using the `APIO_HOME` environment variable. The location of the packages directory can be set using the `APIO_PACKAGES` environment variable.
54 |
55 |
Examples
56 |
57 | ```
58 | apio info system # Show system information
59 | ```
60 |
61 |
Options
62 |
63 | ```
64 | -h, --help Show this message and exit
65 | ```
66 |
67 | ---
68 |
69 | ## apio info colors
70 |
71 | The `apio info colors` command shows how ANSI colors are rendered on your
72 | system, which helps diagnose color-related issues.
73 |
74 | > The command shows colors even if the current theme is `no-colors`.
75 |
76 |
84 |
85 | ```
86 | -h, --help Show this message and exit
87 | ```
88 |
89 | ---
90 |
91 | ## apio info themes
92 |
93 | The command `apio info themes` shows the colors of the Apio themes. It
94 | can be used to select the theme that works the best for you. Type
95 | `apio preferences -h` for information on our to select a theme.
96 |
97 | > The command shows the themes colors even if the current theme is `no-colors`.
98 |
99 |
Examples
100 | ```
101 | apio info themes # Show themes colors
102 | apio inf col -p # Using shortcuts.
103 | ```
104 |
105 |
Options
106 | ```
107 | -h, --help Show this message and exit.
108 | ```
109 |
110 |
Example output
111 |
112 | 
113 |
114 | ## apio info commands
115 |
116 | The command `apio info commands` lists the the available apio commands
117 | in a table format. If the option `--docs` is specified, the command
118 | outputs the list as a markdown document that is used to automatically
119 | update the Apio documentation.
120 |
121 |