Skip to content

Commit

Permalink
Merge pull request #490 from zapta/develop
Browse files Browse the repository at this point in the history
Tweaked apio messages and updated its README.md
  • Loading branch information
Obijuan authored Dec 8, 2024
2 parents 32829d0 + 6316378 commit 750a49b
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 68 deletions.
84 changes: 56 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,71 @@
[![Build Status][build-image]][build-url]
[![License][license-image]][license-url]

![][linux-logo]
 
![][macosx-logo]
 
![][windows-logo]
 
![][ubuntu-logo]
 
![][raspbian-logo]

Open source **ecosystem for open FPGA boards**
![][linux-logo]   ![][macosx-logo]   ![][windows-logo]   ![][ubuntu-logo]   ![][raspbian-logo]


**TL;DR, Apio is an extremly easy to install and use open-source toolbox for programming FPGA boards.**

## What is Apio?

Apio is a **multiplatform** toolbox with **static** pre-built packages to verify, synthesize, simulate and upload your verilog designs into the supported **FPGA boards**
* Apio is an **extremly easy to use** toolbox for FPGA programming.
* Apio is **easy to install**, no more dealing with 'toolcahins', licenses, scripts, and makefiles.
* Apio runs on a wide range of platforms, **Linux, Windows, Mac, and more**.
* Apio is **open source and free to use**.
* Apio supports **all aspects of FPGA developement cycles**, including building, simulation, testing, and uploading a design.
* **Apio commands are very simple,** for example, ``apio build`` to build, ``apio test`` to test end ``apio upload`` to upload.
* Apio can be used with **any text editor** and also **playes well with Visual Studio Code and github**.
* Apio supports out of the **more than 80 boards** and **custom boards can be easily added**.
* Apio provides out of the box tens of simple **project examples ready to build and upload**.
* Apio currently supports the ``ICE40`` and ``ECP5`` FPGA architecture with ``GOWIN`` architecture in the works.

## What??????
## The Apio phylosophy

Apio makes **extremely easy** the process of working with **FPGAs**. Go from **scratch** to having a **blinky LED** in your FPGA board in minutes! This is because it is based only on **Free/Libre Open Source Software** (FLOSS). Just install it and use it as you want
Apio makes **extremely easy** the process of working with **FPGAs**. Go from **scratch** to having a **blinky LED** in your FPGA board in minutes! This is because it is was designed for ease of use and uses only **Free/Libre Open Source Software** (FLOSS). Just install it and use it as you want.


In this animation you can see the whole process of testing the Blinky led circuit: Just type one command and the circuit will be synthesized, and uploaded into the FPGA
In this animation you can see the whole process of testing the Blinky led circuit: Just type the command ``apio upload`` and the circuit will be synthesized, and uploaded into the FPGA

![](https://github.com/FPGAwars/Apio-wiki/raw/main/wiki/Quick-start/apio-alhambra-II-01.gif)

Think of Apio as a small **FPGA distribution**, which **collects** and **packages** **FLOSS toolchains for FPGAs**. You can install packages in **Linux**, **Mac** and **Windows** for synthesizing hardware, verifying and simulating from verilog files

The goal is making it **very easy** to start with FPGAs

As the user **gh02t** said in this post on [Hacker-news](https://news.ycombinator.com/item?id=17912510):
> Apio is a command line tool that automates installing the toolchain for your FPGA and running it. It just simplifies things, you don't have to use it if you'd rather call the individual tools for synthesis, P&R, simulation etc. It'd be reasonable to think of it as akin to a very smart Makefile combined with an automatic package manager, specialized to FPGAs (it's based on PlatformIO). It's nice when you're still kind of getting oriented, because you don't need to know how to set up and invoke the different tools... just call `apio build` or `apio simulate`
## Apio and higher level tools
## Sample Apio Commands

Apio has a **command line interface** (CLI). It is the **building block** for other **higher level tools**, like [Icestudio](https://icestudio.io/), [Apio-IDE](https://github.com/FPGAwars/apio-ide) or working with FPGAs from IDEs such as [Visual Studio Code](https://code.visualstudio.com/)
Below are typical apio commands that are used during the project developement cycle. The commands
are simple and intuitive.

```
# Create a project
apio examples -f alhambra-ii/ledon # Fetch the files of an example
# Build
apio build # Build the project
apio upload # Upload the design to the FPGA board
# Verification
apio lint # Inspect the source code for issues.
apio test # Run the testbench
apio sim ledon_tb.v # Simulate the testbench and open a graphical viewer.
```

## Apio and higher level tools

While many use Apio as a stand alone text based CLI toolbox, it can also be used with higher level graphical tools such as [Icestudio](https://icestudio.io/):

### A circuit in Icestdio

![](https://github.com/FPGAwars/Apio-wiki/raw/main/wiki/Introduction/icestudio-example.png)


### A verilog circuit in VSCode
## Apio in the media

[Shawn Hymel's](https://shawnhymel.com/) excellent series on FPGA programming is based on **Apio** and the the Icestick board

[![Introduction to FPGA YouTube Series](https://raw.githubusercontent.com/ShawnHymel/introduction-to-fpga/main/images/Intro%20to%20FPGA%20Part%201_Thumbnail.png)](https://www.youtube.com/watch?v=lLg1AgA2Xoo&list=PLEBQazB0HUyT1WmMONxRZn9NmQ_9CIKhb)

![](https://github.com/FPGAwars/Apio-wiki/raw/main/wiki/Introduction/vscode-example.png)

## Documentation

Expand All @@ -71,12 +91,18 @@ Find all the information on this [WIKI PAGE](https://github.com/FPGAwars/apio/wi

## Credits

* Apio is uses the [YosysHQ](https://www.yosyshq.com) open source toolchain.

* APIO was inspired by [PlatformIO](https://github.com/platformio/platformio).

* [FPGAwars](http://fpgawars.github.io/) community has developed this project in a voluntary and altruistic way since 11/2016.


* Apio is implemented in Python and using the packages [Click](https://pypi.org/project/click/), and [Scons](https://pypi.org/project/SCons/).

* [BQ](https://www.bq.com) sponsored this project from 02/2016 to 11/2016. Thanks.


## License

Licensed under [GPL 2.0](http://opensource.org/licenses/GPL-2.0) and [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
Expand All @@ -87,6 +113,8 @@ Licensed under [GPL 2.0](http://opensource.org/licenses/GPL-2.0) and [Creative C

<!-- Badges and URLs -->

[apio-logo]: https://github.com/FPGAwars/Apio-wiki/raw/main/wiki/Logos/Apio-github.png

[pypi-image]: https://img.shields.io/pypi/v/apio
[pypi-url]: https://pypi.org/project/apio/

Expand All @@ -96,11 +124,11 @@ Licensed under [GPL 2.0](http://opensource.org/licenses/GPL-2.0) and [Creative C
[license-image]: http://img.shields.io/:license-gpl-blue.svg
[license-url]: (http://opensource.org/licenses/GPL-2.0)

[apio-logo]: https://github.com/FPGAwars/Apio-wiki/raw/main/wiki/Logos/Apio-github.png
[linux-logo]: https://github.com/FPGAwars/Apio-wiki/blob/main/wiki/Logos/linux.png
[macosx-logo]: https://github.com/FPGAwars/Apio-wiki/blob/main/wiki/Logos/macosx.png
[windows-logo]: https://github.com/FPGAwars/Apio-wiki/blob/main/wiki/Logos/windows.png
[ubuntu-logo]: https://github.com/FPGAwars/Apio-wiki/blob/main/wiki/Logos/ubuntu.png
[raspbian-logo]: https://github.com/FPGAwars/Apio-wiki/blob/main/wiki/Logos/raspbian.png

[linux-logo]: https://raw.githubusercontent.com/FPGAwars/Apio-wiki/refs/heads/main/wiki/Logos/linux.png
[macosx-logo]: https://raw.githubusercontent.com/FPGAwars/Apio-wiki/refs/heads/main/wiki/Logos/macosx.png
[windows-logo]: https://raw.githubusercontent.com/FPGAwars/Apio-wiki/refs/heads/main/wiki/Logos/windows.png
[ubuntu-logo]: https://raw.githubusercontent.com/FPGAwars/Apio-wiki/refs/heads/main/wiki/Logos/ubuntu.png
[raspbian-logo]: https://raw.githubusercontent.com/FPGAwars/Apio-wiki/refs/heads/main/wiki/Logos/raspbian.png

[wiki]: https://github.com/FPGAwars/apio/wiki
19 changes: 11 additions & 8 deletions apio/cmd_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import sys
from typing import Mapping, List, Tuple, Any, Dict, Union
import click
from apio import util


# This text marker is inserted into the help text to indicates
Expand Down Expand Up @@ -139,9 +140,9 @@ def check_at_most_one_param(
canonical_aliases = _params_ids_to_aliases(
cmd_ctx, specified_param_ids
)
aliases_str = ", ".join(canonical_aliases)
aliases_str = util.list_plurality(canonical_aliases, "and")
fatal_usage_error(
cmd_ctx, f"[{aliases_str}] cannot be combined together."
cmd_ctx, f"{aliases_str} cannot be combined together."
)


Expand All @@ -165,15 +166,17 @@ def check_exactly_one_param(
if len(specified_param_ids) < 1:
# -- User specified Less flags than required.
canonical_aliases = _params_ids_to_aliases(cmd_ctx, param_ids)
aliases_str = ", ".join(canonical_aliases)
fatal_usage_error(cmd_ctx, f"Specify one of [{aliases_str}].")
aliases_str = util.list_plurality(canonical_aliases, "or")
fatal_usage_error(cmd_ctx, f"specify one of {aliases_str}.")
else:
# -- User specified more flags than allowed.
canonical_aliases = _params_ids_to_aliases(
cmd_ctx, specified_param_ids
)
aliases_str = ", ".join(canonical_aliases)
fatal_usage_error(cmd_ctx, f"Specify only one of [{aliases_str}].")
aliases_str = util.list_plurality(canonical_aliases, "and")
fatal_usage_error(
cmd_ctx, f"{aliases_str} cannot be combined together."
)


def check_at_least_one_param(
Expand All @@ -193,9 +196,9 @@ def check_at_least_one_param(
# If more 2 or more print an error and exit.
if len(specified_param_ids) < 1:
canonical_aliases = _params_ids_to_aliases(cmd_ctx, param_ids)
aliases_str = ", ".join(canonical_aliases)
aliases_str = util.list_plurality(canonical_aliases, "or")
fatal_usage_error(
cmd_ctx, f"At list one of [{aliases_str}] must be specified."
cmd_ctx, f"at list one of {aliases_str} must be specified."
)


Expand Down
2 changes: 1 addition & 1 deletion apio/commands/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
apio examples --list # List all examples
apio examples -l | grep -i icezum # Filter examples.
apio examples -f icezum/leds # Fetch example files
apio examples -s icezum/leds # Fetch example directory
apio examples -d icezum/leds # Fetch example directory
apio examples -d icezum # Fetch all board examples
"""

Expand Down
4 changes: 4 additions & 0 deletions apio/commands/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ def cli(
click.secho("Apio version ", nl=False)
click.secho(importlib.metadata.version("apio"), fg="cyan")

# -- Apio version.
click.secho("Python version ", nl=False)
click.secho(util.get_python_version(), fg="cyan")

# -- Print platform id.
click.secho("Platform id ", nl=False)
click.secho(apio_ctx.platform_id, fg="cyan")
Expand Down
22 changes: 3 additions & 19 deletions apio/managers/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,6 @@
from apio import pkg_util
from apio.apio_context import ApioContext

# -- Error messages
EXAMPLE_NOT_FOUND_MSG = """
Warning: this example does not exist
Use `apio examples -l` to list all the available examples"""

USAGE_EXAMPLE = """
To fetch example files:
apio examples -f <example-name>
Example of use:
apio examples -f icesum/leds
Type 'apio examples -h' for more details.
"""


@dataclass
class ExampleInfo:
Expand Down Expand Up @@ -138,7 +123,6 @@ def list_examples(self) -> None:
# -- For a terminal, emit additional summary.
if output_config.terminal_mode():
click.secho(f"Total: {len(examples)}")
click.secho(USAGE_EXAMPLE, fg="green")

return 0

Expand Down Expand Up @@ -166,7 +150,7 @@ def copy_example_dir(self, example: str, project_dir: Path, sayno: bool):

# -- If the source example path is not a folder... it is an error
if not src_example_path.is_dir():
click.secho(EXAMPLE_NOT_FOUND_MSG, fg="yellow")
click.secho(f"Error: example [{example}] not found.", fg="red")
return 1

# -- The destination path is a folder...It means that the
Expand Down Expand Up @@ -221,9 +205,9 @@ def copy_example_files(self, example: str, project_dir: Path, sayno: bool):
# -- Build the source example path (where the example was installed)
src_example_path = self.examples_dir / example

# -- If the source example path is not a folder... it is an error
# -- If the source example path is not a folder. it is an error
if not src_example_path.is_dir():
click.secho(EXAMPLE_NOT_FOUND_MSG, fg="yellow")
click.secho(f"Error: example [{example}] not found.", fg="red")
return 1

# -- Copy the example files!!
Expand Down
19 changes: 18 additions & 1 deletion apio/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import shutil
from enum import Enum
from dataclasses import dataclass
from typing import Optional, Any, Tuple
from typing import Optional, Any, Tuple, List
import subprocess
from threading import Thread
from pathlib import Path
Expand Down Expand Up @@ -488,3 +488,20 @@ def plurality(obj: Any, singular: str, plural: str = None) -> str:
if plural is None:
plural = singular + "s"
return f"{n} {plural}"


def list_plurality(str_list: List[str], conjunction: str) -> str:
"""Format a list as a human friendly string."""
# -- This is a programming error. Not a user error.
assert str_list, "list_plurarlity expect len() >= 1."

# -- Handle the case of a single item.
if len(str_list) == 1:
return str_list[0]

# -- Handle the case of 2 items.
if len(str_list) == 2:
return f"{str_list[0]} {conjunction} {str_list[1]}"

# -- Handle the case of three or more items.
return ", ".join(str_list[:-1]) + f", {conjunction} {str_list[-1]}"
6 changes: 3 additions & 3 deletions test/commands/test_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def test_drivers(apio_runner: ApioRunner):
result = sb.invoke_apio_cmd(apio_drivers)
assert result.exit_code == 1
assert (
"Error: Specify one of [--ftdi-install, --ftdi-uninstall, "
"--serial-install, --serial-uninstall]" in result.output
"specify one of --ftdi-install, --ftdi-uninstall, "
"--serial-install, or --serial-uninstall" in result.output
)

# -- Execute "apio --ftdi-install, --serial-install"
Expand All @@ -27,6 +27,6 @@ def test_drivers(apio_runner: ApioRunner):
)
assert result.exit_code == 1, result.output
assert (
"Error: Specify only one of [--ftdi-install, --serial-install]"
"Error: --ftdi-install and --serial-install cannot be combined"
in result.output
)
2 changes: 1 addition & 1 deletion test/commands/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_examples(apio_runner: ApioRunner):
result = sb.invoke_apio_cmd(apio_examples)
assert result.exit_code == 1, result.output
assert (
"Error: Specify one of [--list, --fetch-dir, --fetch-files]"
"specify one of --list, --fetch-dir, or --fetch-files"
in result.output
)

Expand Down
2 changes: 1 addition & 1 deletion test/commands/test_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_packages(apio_runner: ApioRunner):
result = sb.invoke_apio_cmd(apio_packages)
assert result.exit_code == 1, result.output
assert (
"Error: Specify one of [--list, --install, --uninstall, --fix]"
"specify one of --list, --install, --uninstall, or --fix"
in result.output
)

Expand Down
5 changes: 2 additions & 3 deletions test/commands/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ def test_system(apio_runner: ApioRunner):
result = sb.invoke_apio_cmd(apio_system)
assert result.exit_code == 1, result.output
assert (
"Specify one of "
"[--lsftdi, --lsusb, --lsserial, --info, --platforms]"
in result.output
"specify one of --lsftdi, --lsusb, --lsserial, --info, "
"or --platforms" in result.output
)

# -- Execute "apio system --lsftdi"
Expand Down
2 changes: 1 addition & 1 deletion test/test_cmd_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ def test_check_at_most_one_param(capsys):
check_at_most_one_param(cmd_ctx, ["_opt1", "_opt2", "_opt3"])
captured = capsys.readouterr()
assert e.value.code == 1
assert "[--opt1, --opt2] cannot be combined together" in captured.out
assert "--opt1 and --opt2 cannot be combined together" in captured.out
26 changes: 24 additions & 2 deletions test/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,34 @@
Tests of scons_util.py
"""

from apio.util import plurality
import pytest
from apio.util import plurality, list_plurality

# pylint: disable=fixme
# TODO: Add more tests.


def test_pluraliry():
"""Tests the plurality function."""
"""Tests the plurality() function."""
# -- Test for ints 1, 2, 3
assert plurality(1, "file") == "1 file"
assert plurality(2, "file") == "2 files"
assert plurality(3, "file") == "3 files"

# -- Test for lengths 1, 2, 3.
assert plurality(["aa"], "file") == "1 file"
assert plurality(["aa", "bb"], "file") == "2 files"
assert plurality(["aa", "bb", "cc"], "file") == "3 files"


def test_list_pluraliry():
"""Tests the list_plurality() function."""

# -- Test for lengths 1, 2, and 3.
assert list_plurality(["aa"], "or") == "aa"
assert list_plurality(["aa", "bb"], "and") == "aa and bb"
assert list_plurality(["aa", "bb", "cc"], "or") == "aa, bb, or cc"

# -- An empty list should trhow an assert exception.
with pytest.raises(AssertionError):
list_plurality([], "or")

0 comments on commit 750a49b

Please sign in to comment.