From d7be39bfdfc5b59f5ab488fbdf81f05dbd9c70a9 Mon Sep 17 00:00:00 2001 From: Zapta Date: Tue, 21 Jan 2025 17:33:05 -0800 Subject: [PATCH 01/12] Moved the examples report logic from the Examples manager to the apio examples list command. --- apio/commands/apio_examples.py | 55 +++++++++++++++++++++++++++++----- apio/managers/examples.py | 45 ---------------------------- 2 files changed, 48 insertions(+), 52 deletions(-) diff --git a/apio/commands/apio_examples.py b/apio/commands/apio_examples.py index a8aef170..fd50f029 100644 --- a/apio/commands/apio_examples.py +++ b/apio/commands/apio_examples.py @@ -7,10 +7,12 @@ # -- Licence GPLv2 """Implementation of 'apio examples' command""" -import sys from pathlib import Path +from typing import List import click -from apio.managers.examples import Examples +from click import secho +from apio.managers import installer +from apio.managers.examples import Examples, ExampleInfo from apio.commands import options from apio.apio_context import ApioContext, ApioContextScope from apio.utils import util @@ -33,6 +35,49 @@ """ +def list_examples(apio_ctx: ApioContext) -> None: + """Print all the examples available. Return a process exit + code, 0 if ok, non zero otherwise.""" + + # -- Make sure that the examples package is installed. + installer.install_missing_packages_on_the_fly(apio_ctx) + + # -- Get list of examples. + examples: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos() + + # -- Get terminal configuration. We format the report differently for + # -- a terminal and for a pipe. + output_config = util.get_terminal_config() + + # -- For terminal, print a header with an horizontal line across the + # -- terminal. + if output_config.terminal_mode: + terminal_seperator_line = "─" * output_config.terminal_width + secho() + secho(terminal_seperator_line) + + # -- For a pipe, determine the max example name length. + max_example_name_len = max(len(x.name) for x in examples) + + # -- Emit the examples + for example in examples: + if output_config.terminal_mode: + # -- For a terminal. Multi lines and colors. + secho(f"{example.name}", fg="cyan", bold=True) + secho(f"{example.description}") + secho(terminal_seperator_line) + else: + # -- For a pipe, single line, no colors. + secho( + f"{example.name:<{max_example_name_len}} | " + f"{example.description}" + ) + + # -- For a terminal, emit additional summary. + if output_config.terminal_mode: + secho(f"Total: {len(examples)}") + + @click.command( name="list", short_help="List the available apio examples.", @@ -44,12 +89,8 @@ def _list_cli(): # -- Create the apio context. apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - # -- Create the examples manager. - examples = Examples(apio_ctx) - # --List all available examples. - exit_code = examples.list_examples() - sys.exit(exit_code) + list_examples(apio_ctx) # ---- apio examples fetch diff --git a/apio/managers/examples.py b/apio/managers/examples.py index 4a628688..0b9ce133 100644 --- a/apio/managers/examples.py +++ b/apio/managers/examples.py @@ -13,7 +13,6 @@ from dataclasses import dataclass from typing import Optional, List, Dict from click import secho, style, echo -from apio.utils import util from apio.apio_context import ApioContext from apio.managers import installer @@ -107,50 +106,6 @@ def get_examples_infos(self) -> List[ExampleInfo]: return examples - def list_examples(self) -> None: - """Print all the examples available. Return a process exit - code, 0 if ok, non zero otherwise.""" - - # -- Make sure that the examples package is installed. - installer.install_missing_packages_on_the_fly(self.apio_ctx) - - # -- Get list of examples. - examples: List[ExampleInfo] = self.get_examples_infos() - - # -- Get terminal configuration. We format the report differently for - # -- a terminal and for a pipe. - output_config = util.get_terminal_config() - - # -- For terminal, print a header with an horizontal line across the - # -- terminal. - if output_config.terminal_mode: - terminal_seperator_line = "─" * output_config.terminal_width - secho() - secho(terminal_seperator_line) - - # -- For a pipe, determine the max example name length. - max_example_name_len = max(len(x.name) for x in examples) - - # -- Emit the examples - for example in examples: - if output_config.terminal_mode: - # -- For a terminal. Multi lines and colors. - secho(f"{example.name}", fg="cyan", bold=True) - secho(f"{example.description}") - secho(terminal_seperator_line) - else: - # -- For a pipe, single line, no colors. - secho( - f"{example.name:<{max_example_name_len}} | " - f"{example.description}" - ) - - # -- For a terminal, emit additional summary. - if output_config.terminal_mode: - secho(f"Total: {len(examples)}") - - return 0 - def count_examples_by_board(self) -> Dict[str, int]: """Returns a dictionary with example count per board. Boards that have no examples are not included in the dictionary.""" From a6f47af3ea3244321f580c80779836119b216cae Mon Sep 17 00:00:00 2001 From: Zapta Date: Tue, 21 Jan 2025 19:07:25 -0800 Subject: [PATCH 02/12] Changed the format of the 'apio examples list' report to match that of 'apio boards' and 'apio fpgas'. --- apio/commands/apio_boards.py | 18 ++----- apio/commands/apio_examples.py | 87 ++++++++++++++++++++++------------ apio/commands/apio_fpgas.py | 44 +++++++---------- apio/managers/examples.py | 18 ++++++- apio/utils/util.py | 18 +++++++ 5 files changed, 110 insertions(+), 75 deletions(-) diff --git a/apio/commands/apio_boards.py b/apio/commands/apio_boards.py index 6bcdab9b..cf78ad62 100644 --- a/apio/commands/apio_boards.py +++ b/apio/commands/apio_boards.py @@ -39,18 +39,8 @@ class Entry: programmer: str def sort_key(self): - """Returns a key for sorting entiries. Primary key is the architecture - by our prefered order, secondary key is board id.""" - # -- Prefer arch order - archs = ["ice40", "ecp5", "gowin"] - # -- Primary key - primary_key = ( - archs.index(self.fpga_arch) - if self.fpga_arch in archs - else len(archs) - ) - # -- Secondary key is board name. - return (primary_key, self.board.lower()) + """A key for sorting the fpga entries in our prefered order.""" + return (util.fpga_arch_sort_key(self.fpga_arch), self.board.lower()) # R0914: Too many local variables (17/15) @@ -98,7 +88,7 @@ def list_boards(apio_ctx: ApioContext, verbose: bool): ) ) - # -- Sort boards by case insensitive board namd. + # -- Sort boards by our prefered order. entries.sort(key=lambda x: x.sort_key()) # -- Compute the columns widths. @@ -135,7 +125,7 @@ def list_boards(apio_ctx: ApioContext, verbose: bool): parts.append(f"{'SPEED':<{fpga_speed_len}}") parts.append(f"{'PROGRAMMER':<{programmer_len}}") - # -- Show the title line. + # -- Print the title line. secho("".join(parts), fg="cyan", bold=True) # -- Print all the boards. diff --git a/apio/commands/apio_examples.py b/apio/commands/apio_examples.py index fd50f029..75c36bc9 100644 --- a/apio/commands/apio_examples.py +++ b/apio/commands/apio_examples.py @@ -8,9 +8,9 @@ """Implementation of 'apio examples' command""" from pathlib import Path -from typing import List +from typing import List, Any import click -from click import secho +from click import secho, style, echo from apio.managers import installer from apio.managers.examples import Examples, ExampleInfo from apio.commands import options @@ -29,68 +29,93 @@ \b Examples: apio examples list # List all examples + apio examples list -v # List with extra information. apio examples list | grep alhambra-ii # Show examples of a specific board. apio examples list | grep -i blink # Show all blinking examples. """ -def list_examples(apio_ctx: ApioContext) -> None: +def examples_sort_key(entry: ExampleInfo) -> Any: + """A key for sorting the fpga entries in our prefered order.""" + return (util.fpga_arch_sort_key(entry.fpga_arch), entry.name) + + +def list_examples(apio_ctx: ApioContext, verbose: bool) -> None: """Print all the examples available. Return a process exit code, 0 if ok, non zero otherwise.""" + # -- Get the output info (terminal vs pipe). + output_config = util.get_terminal_config() # -- Make sure that the examples package is installed. installer.install_missing_packages_on_the_fly(apio_ctx) # -- Get list of examples. - examples: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos() + entries: List[ExampleInfo] = Examples(apio_ctx).get_examples_infos() - # -- Get terminal configuration. We format the report differently for - # -- a terminal and for a pipe. - output_config = util.get_terminal_config() + # -- Sort boards by case insensitive board namd. + entries.sort(key=examples_sort_key) - # -- For terminal, print a header with an horizontal line across the - # -- terminal. - if output_config.terminal_mode: - terminal_seperator_line = "─" * output_config.terminal_width - secho() - secho(terminal_seperator_line) + # Compute field lengths + margin = 2 + name_len = max(len(x.name) for x in entries) + margin + fpga_arch_len = max(len(x.fpga_arch) for x in entries) + margin + fpga_part_num_len = max(len(x.fpga_part_num) for x in entries) + margin - # -- For a pipe, determine the max example name length. - max_example_name_len = max(len(x.name) for x in examples) + # -- Construct the title fields. + parts = [] + parts.append(f"{'EXAMPLE':<{name_len}}") + if verbose: + parts.append(f"{'ARCH':<{fpga_arch_len}}") + parts.append(f"{'PART-NUM':<{fpga_part_num_len}}") + parts.append("DESCRIPTION") + + # -- Print the title + secho("".join(parts), fg="cyan", bold="True") # -- Emit the examples - for example in examples: - if output_config.terminal_mode: - # -- For a terminal. Multi lines and colors. - secho(f"{example.name}", fg="cyan", bold=True) - secho(f"{example.description}") - secho(terminal_seperator_line) - else: - # -- For a pipe, single line, no colors. + last_arch = None + for entry in entries: + # -- Seperation before each archictecture group, unless piped out. + if last_arch != entry.fpga_arch and output_config.terminal_mode: + echo("") + secho(f"{entry.fpga_arch.upper()}", fg="magenta", bold=True) + last_arch = entry.fpga_arch + + # -- Construct the fpga fields. + parts = [] + parts.append(style(f"{entry.name:<{name_len}}", fg="cyan")) + if verbose: + parts.append(f"{entry.fpga_arch:<{fpga_arch_len}}") + parts.append(f"{entry.fpga_part_num:<{fpga_part_num_len}}") + parts.append(f"{entry.description}") + + # -- Print the fpga line. + echo("".join(parts)) + + # -- Show summary. + if output_config.terminal_mode: + secho(f"Total of {util.plurality(entries, 'example')}") + if not verbose: secho( - f"{example.name:<{max_example_name_len}} | " - f"{example.description}" + "Run 'apio examples -v' for additional columns.", fg="yellow" ) - # -- For a terminal, emit additional summary. - if output_config.terminal_mode: - secho(f"Total: {len(examples)}") - @click.command( name="list", short_help="List the available apio examples.", help=APIO_EXAMPLES_LIST_HELP, ) -def _list_cli(): +@options.verbose_option +def _list_cli(verbose: bool): """Implements the 'apio examples list' command group.""" # -- Create the apio context. apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) # --List all available examples. - list_examples(apio_ctx) + list_examples(apio_ctx, verbose) # ---- apio examples fetch diff --git a/apio/commands/apio_fpgas.py b/apio/commands/apio_fpgas.py index 6b69401a..d3d30257 100644 --- a/apio/commands/apio_fpgas.py +++ b/apio/commands/apio_fpgas.py @@ -35,18 +35,8 @@ class Entry: fpga_speed: str def sort_key(self): - """A kery for sorting entries. Primary key is architecture, by - our prefer order, and secondary is fpga id.""" - # -- Prefer arch order - archs = ["ice40", "ecp5", "gowin"] - # -- Primary key - primary_key = ( - archs.index(self.fpga_arch) - if self.fpga_arch in archs - else len(archs) - ) - # -- Secondary key is board name. - return (primary_key, self.fpga.lower()) + """A key for sorting the fpga entries in our prefered order.""" + return (util.fpga_arch_sort_key(self.fpga_arch), self.fpga.lower()) # pylint: disable=too-many-locals @@ -90,7 +80,7 @@ def list_fpgas(apio_ctx: ApioContext, verbose: bool): ) ) - # -- Sort boards by case insensitive board namd. + # -- Sort boards by our prefered order. entries.sort(key=lambda x: x.sort_key()) # -- Compute field lengths @@ -121,34 +111,32 @@ def list_fpgas(apio_ctx: ApioContext, verbose: bool): # -- Iterate and print the fpga entries in the list. last_arch = None - for entries in entries: + for entry in entries: # -- Seperation before each archictecture group, unless piped out. - if last_arch != entries.fpga_arch and output_config.terminal_mode: + if last_arch != entry.fpga_arch and output_config.terminal_mode: echo("") - secho(f"{entries.fpga_arch.upper()}", fg="magenta", bold=True) - last_arch = entries.fpga_arch + secho(f"{entry.fpga_arch.upper()}", fg="magenta", bold=True) + last_arch = entry.fpga_arch # -- Construct the fpga fields. parts = [] - parts.append(style(f"{entries.fpga:<{fpga_len}}", fg="cyan")) - board_count = ( - f"{entries.board_count:>3}" if entries.board_count else "" - ) + parts.append(style(f"{entry.fpga:<{fpga_len}}", fg="cyan")) + board_count = f"{entry.board_count:>3}" if entry.board_count else "" parts.append(f"{board_count:<{board_count_len}}") - parts.append(f"{entries.fpga_arch:<{fpga_arch_len}}") - parts.append(f"{entries.fpga_part_num:<{fpga_part_num_len}}") - parts.append(f"{entries.fpga_size:<{fpga_size_len}}") + parts.append(f"{entry.fpga_arch:<{fpga_arch_len}}") + parts.append(f"{entry.fpga_part_num:<{fpga_part_num_len}}") + parts.append(f"{entry.fpga_size:<{fpga_size_len}}") if verbose: - parts.append(f"{entries.fpga_type:<{fpga_type_len}}") - parts.append(f"{entries.fpga_pack:<{fpga_pack_len}}") - parts.append(f"{entries.fpga_speed:<{fpga_speed_len}}") + parts.append(f"{entry.fpga_type:<{fpga_type_len}}") + parts.append(f"{entry.fpga_pack:<{fpga_pack_len}}") + parts.append(f"{entry.fpga_speed:<{fpga_speed_len}}") # -- Print the fpga line. echo("".join(parts)) # -- Show summary. if output_config.terminal_mode: - secho(f"Total of {util.plurality(apio_ctx.fpgas, 'fpga')}") + secho(f"Total of {util.plurality(entries, 'fpga')}") if not verbose: secho("Run 'apio fpgas -v' for additional columns.", fg="yellow") diff --git a/apio/managers/examples.py b/apio/managers/examples.py index 0b9ce133..10a131c9 100644 --- a/apio/managers/examples.py +++ b/apio/managers/examples.py @@ -25,6 +25,8 @@ class ExampleInfo: example_dir_name: str path: PosixPath description: str + fpga_arch: str + fpga_part_num: str @property def name(self) -> str: @@ -94,10 +96,22 @@ def get_examples_infos(self) -> List[ExampleInfo]: else: description = "" + # -- Extract the fpga arch and part number, with "" as + # -- default value if not found. + board_info = self.apio_ctx.boards.get(board_dir.name, {}) + fpga_id = board_info.get("fpga", "") + fpga_info = self.apio_ctx.fpgas.get(fpga_id, {}) + fpga_arch = fpga_info.get("arch", "") + fpga_part_num = fpga_info.get("part_num", "") + # -- Append this example to the list. - # name = f"{board_dir.name}/{example_dir.name}" example_info = ExampleInfo( - board_dir.name, example_dir.name, example_dir, description + board_dir_name=board_dir.name, + example_dir_name=example_dir.name, + path=example_dir, + description=description, + fpga_arch=fpga_arch, + fpga_part_num=fpga_part_num, ) examples.append(example_info) diff --git a/apio/utils/util.py b/apio/utils/util.py index 239bc6f3..7893c32d 100644 --- a/apio/utils/util.py +++ b/apio/utils/util.py @@ -719,3 +719,21 @@ def split( # --All done. return s + + +def fpga_arch_sort_key(fpga_arch: str) -> Any: + """Given an fpga arch name such as 'ice40', return a sort key + got force our prefered order of sorthing by architecutre. Used in + reports such as examples, fpgas, and boards.""" + + # -- The prefered order of architectures, Add more if adding new + # -- architectures. + archs = ["ice40", "ecp5", "gowin"] + + # -- Primary key with prefered architecuted first and in the + # -- prefered order. + primary_key = archs.index(fpga_arch) if fpga_arch in archs else len(archs) + + # -- Construct the key, unknown architectures list at the end by + # -- lexicographic order. + return (primary_key, fpga_arch) From 958baf2901f31afbc954df265ba4380ca349a32b Mon Sep 17 00:00:00 2001 From: Zapta Date: Tue, 21 Jan 2025 19:26:00 -0800 Subject: [PATCH 03/12] Add fpga size to examples list report. --- apio/commands/apio_examples.py | 5 ++++- apio/managers/examples.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apio/commands/apio_examples.py b/apio/commands/apio_examples.py index 75c36bc9..a4daf602 100644 --- a/apio/commands/apio_examples.py +++ b/apio/commands/apio_examples.py @@ -61,13 +61,15 @@ def list_examples(apio_ctx: ApioContext, verbose: bool) -> None: name_len = max(len(x.name) for x in entries) + margin fpga_arch_len = max(len(x.fpga_arch) for x in entries) + margin fpga_part_num_len = max(len(x.fpga_part_num) for x in entries) + margin + fpga_size_len = max(len(x.fpga_size) for x in entries) + margin + 1 # -- Construct the title fields. parts = [] - parts.append(f"{'EXAMPLE':<{name_len}}") + parts.append(f"{'BOARD/EXAMPLE':<{name_len}}") if verbose: parts.append(f"{'ARCH':<{fpga_arch_len}}") parts.append(f"{'PART-NUM':<{fpga_part_num_len}}") + parts.append(f"{'SIZE':<{fpga_size_len}}") parts.append("DESCRIPTION") # -- Print the title @@ -88,6 +90,7 @@ def list_examples(apio_ctx: ApioContext, verbose: bool) -> None: if verbose: parts.append(f"{entry.fpga_arch:<{fpga_arch_len}}") parts.append(f"{entry.fpga_part_num:<{fpga_part_num_len}}") + parts.append(f"{entry.fpga_size:<{fpga_size_len}}") parts.append(f"{entry.description}") # -- Print the fpga line. diff --git a/apio/managers/examples.py b/apio/managers/examples.py index 10a131c9..6c7f879d 100644 --- a/apio/managers/examples.py +++ b/apio/managers/examples.py @@ -27,6 +27,7 @@ class ExampleInfo: description: str fpga_arch: str fpga_part_num: str + fpga_size: str @property def name(self) -> str: @@ -103,6 +104,7 @@ def get_examples_infos(self) -> List[ExampleInfo]: fpga_info = self.apio_ctx.fpgas.get(fpga_id, {}) fpga_arch = fpga_info.get("arch", "") fpga_part_num = fpga_info.get("part_num", "") + fpga_size = fpga_info.get("size", "") # -- Append this example to the list. example_info = ExampleInfo( @@ -112,6 +114,7 @@ def get_examples_infos(self) -> List[ExampleInfo]: description=description, fpga_arch=fpga_arch, fpga_part_num=fpga_part_num, + fpga_size=fpga_size, ) examples.append(example_info) From 3632174f7b51a46ca2994882bead3183b3d8b3dd Mon Sep 17 00:00:00 2001 From: Zapta Date: Tue, 21 Jan 2025 22:48:56 -0800 Subject: [PATCH 04/12] Moved as much as possible code from the SConstruct file to the handlerls .py file. --- apio/scons/SConstruct | 49 ++++--------------------------- apio/scons/scons_handler.py | 48 ++++++++++++++++++++++++++++-- test/integration/test_examples.py | 2 +- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/apio/scons/SConstruct b/apio/scons/SConstruct index 2a543767..ca011856 100644 --- a/apio/scons/SConstruct +++ b/apio/scons/SConstruct @@ -1,4 +1,4 @@ -"""Apio's scons handler dispatcher.""" +"""Apio's scons handler entry point.""" # -*- coding: utf-8 -*- # -- This file is part of the Apio project @@ -6,52 +6,13 @@ # -- Authors Juan Gonzáles, Jesús Arroyo # -- Licence GPLv2 -import os -import sys -import debugpy -import click -from google.protobuf import text_format -from SCons.Script import ARGUMENTS, COMMAND_LINE_TARGETS -from apio.scons.apio_env import ApioEnv -from apio.scons.scons_handler import SconsHandler -from apio.scons.plugin_ice40 import PluginIce40 -from apio.scons.plugin_ecp5 import PluginEcp5 -from apio.scons.plugin_gowin import PluginGowin -from apio.scons.plugin_util import maybe_wait_for_remote_debugger -from apio.proto.apio_pb2 import SconsParams, ICE40, ECP5, GOWIN +from apio.scons.plugin_util import maybe_wait_for_remote_debugger +from apio.scons.scons_handler import SconsHandler # -- If system env var APIO_SCONS_DEBUGGER is defined, regardless of its value, # -- we wait on a remote debugger to be attached, e.g. from Visual Studio Code. maybe_wait_for_remote_debugger("APIO_SCONS_DEBUGGER") -# -- Read the text of the scons params file. -with open("_build/scons.params", "r", encoding="utf8") as f: - proto_text = f.read() - -# -- Parse the text into SconsParams object. -params:SconsParams = text_format.Parse(proto_text, SconsParams()) - -# -- Compare the params timestamp to the timestamp in the command. -timestamp = ARGUMENTS["timestamp"] -assert params.timestamp == timestamp - - -# -- Create the apio environment. -apio_env = ApioEnv(COMMAND_LINE_TARGETS, params) - -# -- Select the plugin. -if params.arch == ICE40: - plugin = PluginIce40(apio_env) -elif params.arch == ECP5: - plugin = PluginEcp5(apio_env) -elif params.arch == GOWIN: - plugin = PluginGowin(apio_env) -else: - print(f"Apio SConstruct dispatch error: unknown arch [{params.arch}]") - sys.exit(1) - - -# -- Create and invoke the handler. -scons = SconsHandler(apio_env, plugin) -scons.execute() +# -- Service this scons request. +SconsHandler.start() diff --git a/apio/scons/scons_handler.py b/apio/scons/scons_handler.py index bb8465f0..558783a4 100644 --- a/apio/scons/scons_handler.py +++ b/apio/scons/scons_handler.py @@ -10,10 +10,15 @@ """Apio scons related utilities..""" - +import sys +from SCons.Script import ARGUMENTS, COMMAND_LINE_TARGETS +from google.protobuf import text_format +from apio.scons.plugin_ice40 import PluginIce40 +from apio.scons.plugin_ecp5 import PluginEcp5 +from apio.scons.plugin_gowin import PluginGowin +from apio.proto.apio_pb2 import SconsParams, ICE40, ECP5, GOWIN from apio.scons.apio_env import ApioEnv, TARGET from apio.scons.plugin_base import PluginBase -from apio.proto.apio_pb2 import SconsParams from apio.scons.plugin_util import ( get_sim_config, get_tests_configs, @@ -40,9 +45,48 @@ class SconsHandler: """Base apio scons handler""" def __init__(self, apio_env: ApioEnv, arch_plugin: PluginBase): + """Do not call directly, use SconsHandler.start().""" self.apio_env = apio_env self.arch_plugin = arch_plugin + @staticmethod + def start() -> None: + """This static method is called from SConstruct to create and + execute an SconsHandler.""" + + # -- Read the text of the scons params file. + with open("_build/scons.params", "r", encoding="utf8") as f: + proto_text = f.read() + + # -- Parse the text into SconsParams object. + params: SconsParams = text_format.Parse(proto_text, SconsParams()) + + # -- Compare the params timestamp to the timestamp in the command. + timestamp = ARGUMENTS["timestamp"] + assert params.timestamp == timestamp + + # -- Create the apio environment. + apio_env = ApioEnv(COMMAND_LINE_TARGETS, params) + + # -- Select the plugin. + if params.arch == ICE40: + plugin = PluginIce40(apio_env) + elif params.arch == ECP5: + plugin = PluginEcp5(apio_env) + elif params.arch == GOWIN: + plugin = PluginGowin(apio_env) + else: + print( + f"Apio SConstruct dispatch error: unknown arch [{params.arch}]" + ) + sys.exit(1) + + # -- Create the handler. + scons_handler = SconsHandler(apio_env, plugin) + + # -- Invoke the handler. This services the scons request. + scons_handler.execute() + def register_builders(self): """Register the builders created by the arch plugin.""" apio_env = self.apio_env diff --git a/test/integration/test_examples.py b/test/integration/test_examples.py index 42abc043..c528abf0 100644 --- a/test/integration/test_examples.py +++ b/test/integration/test_examples.py @@ -25,7 +25,7 @@ def test_examples(apio_runner: ApioRunner): result = sb.invoke_apio_cmd(apio, ["examples", "list"]) sb.assert_ok(result) assert "alhambra-ii/ledon" in result.output - assert "Hello world for the Alhambra-II board" in result.output + assert "Turning on a led" in result.output # -- 'apio examples fetch alhambra-ii/ledon' result = sb.invoke_apio_cmd( From 03a1310c7a9e4361fd0e13014d03310eecab06c5 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 14:16:22 -0800 Subject: [PATCH 05/12] Restructured the apio drivers commands per https://github.com/FPGAwars/apio/issues/549#issuecomment-2607985902 --- COMMANDS.md | 171 +++++++++++++----------- apio/commands/apio_drivers.py | 47 +------ apio/commands/apio_drivers_ftdi.py | 150 --------------------- apio/commands/apio_drivers_install.py | 108 +++++++++++++++ apio/commands/apio_drivers_list.py | 152 +++++++++++++++++++++ apio/commands/apio_drivers_serial.py | 147 -------------------- apio/commands/apio_drivers_uninstall.py | 109 +++++++++++++++ 7 files changed, 470 insertions(+), 414 deletions(-) delete mode 100644 apio/commands/apio_drivers_ftdi.py create mode 100644 apio/commands/apio_drivers_install.py create mode 100644 apio/commands/apio_drivers_list.py delete mode 100644 apio/commands/apio_drivers_serial.py create mode 100644 apio/commands/apio_drivers_uninstall.py diff --git a/COMMANDS.md b/COMMANDS.md index be267eff..c5dc1109 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -5,15 +5,16 @@ * [apio clean](#apio-clean) - Delete the apio generated files. * [apio create](#apio-create) - Create an apio.ini project file. * [apio drivers](#apio-drivers) - Manage the operating system drivers. - * [apio drivers ftdi](#apio-drivers-ftdi) - Manage the ftdi drivers. - * [apio drivers ftdi install](#apio-drivers-ftdi-install) - Install the ftdi drivers. - * [apio drivers ftdi list](#apio-drivers-ftdi-list) - List the connected ftdi devices. - * [apio drivers ftdi uninstall](#apio-drivers-ftdi-uninstall) - Uninstall the ftdi drivers. - * [apio drivers lsusb](#apio-drivers-lsusb) - List connected USB devices. - * [apio drivers serial](#apio-drivers-serial) - Manage the serial drivers. - * [apio drivers serial install](#apio-drivers-serial-install) - Install the serial drivers. - * [apio drivers serial list](#apio-drivers-serial-list) - List the connected serial devices. - * [apio drivers serial uninstall](#apio-drivers-serial-uninstall) - Uninstall the serial drivers. + * [apio drivers install](#apio-drivers-install) - Install drivers. + * [apio drivers install ftdi](#apio-drivers-install-ftdi) - Install the ftdi drivers. + * [apio drivers install serial](#apio-drivers-install-serial) - Install the serial drivers. + * [apio drivers list](#apio-drivers-list) - List system drivers. + * [apio drivers list ftdi](#apio-drivers-list-ftdi) - List the connected ftdi devices. + * [apio drivers list serial](#apio-drivers-list-serial) - List the connected serial devices. + * [apio drivers list usb](#apio-drivers-list-usb) - List connected USB devices. + * [apio drivers uninstall](#apio-drivers-uninstall) - Uninstall drivers. + * [apio drivers uninstall ftdi](#apio-drivers-uninstall-ftdi) - Uninstall the ftdi drivers. + * [apio drivers uninstall serial](#apio-drivers-uninstall-serial) - Uninstall the serial drivers. * [apio examples](#apio-examples) - List and fetch apio examples. * [apio examples fetch](#apio-examples-fetch) - Fetch the files of an example. * [apio examples fetch-board](#apio-examples-fetch-board) - Fetch all examples of a board. @@ -208,45 +209,43 @@ Options: -h, --help Show this message and exit. Subcommands: - apio drivers ftdi Manage the ftdi drivers. - apio drivers serial Manage the serial drivers. - apio drivers lsusb List connected USB devices. + apio drivers list List system drivers. + apio drivers install Install drivers. + apio drivers uninstall Uninstall drivers. ```
-### apio drivers ftdi +### apio drivers install ``` -Usage: apio drivers ftdi [OPTIONS] COMMAND [ARGS]... +Usage: apio drivers install [OPTIONS] COMMAND [ARGS]... - The command group 'apio drivers ftdi' includes subcommands that help manage - the FTDI drivers on your system. + The command group 'apio drivers install' includes subcommands that that + install system drivers that are used to upload designs to FPGA boards. Options: -h, --help Show this message and exit. Subcommands: - apio drivers ftdi install Install the ftdi drivers. - apio drivers ftdi uninstall Uninstall the ftdi drivers. - apio drivers ftdi list List the connected ftdi devices. + apio drivers install ftdi Install the ftdi drivers. + apio drivers install serial Install the serial drivers. ```
-### apio drivers ftdi install +### apio drivers install ftdi ``` -Usage: apio drivers ftdi install [OPTIONS] +Usage: apio drivers install ftdi [OPTIONS] - The command 'apio drivers ftdi install' installs on your system the FTDI + The command 'apio drivers install ftdi' installs on your system the FTDI drivers required by some FPGA boards. Examples: - apio drivers ftdi install # Install the ftdi drivers. - apio drivers ftdi uinstall # Uinstall the ftdi drivers. + apio drivers install ftdi # Install the ftdi drivers. Options: -h, --help Show this message and exit. @@ -254,17 +253,54 @@ Options:
-### apio drivers ftdi list +### apio drivers install serial ``` -Usage: apio drivers ftdi list [OPTIONS] +Usage: apio drivers install serial [OPTIONS] - The command 'apio drivers ftdi list' displays the FTDI devices currently + The command ‘apio drivers install serial’ installs the necessary serial + drivers on your system, as required by certain FPGA boards. + + Examples: + apio drivers install serial # Install the serial drivers. + +Options: + -h, --help Show this message and exit. +``` + +
+ +### apio drivers list + +``` +Usage: apio drivers list [OPTIONS] COMMAND [ARGS]... + + The command group 'apio drivers list' includes subcommands that that lists + system drivers that are used with FPGA boards. + +Options: + -h, --help Show this message and exit. + +Subcommands: + apio drivers list ftdi List the connected ftdi devices. + apio drivers list serial List the connected serial devices. + apio drivers list usb List connected USB devices. + +``` + +
+ +### apio drivers list ftdi + +``` +Usage: apio drivers list ftdi [OPTIONS] + + The command 'apio drivers list ftdi' displays the FTDI devices currently connected to your computer. It is useful for diagnosing FPGA board connectivity issues. Examples: - apio drivers ftdi list # List the ftdi devices. + apio drivers list ftdi # List the ftdi devices. [Hint] This command uses the lsftdi utility, which can also be invoked directly with the 'apio raw -- lsftdi ' command. @@ -275,17 +311,19 @@ Options:
-### apio drivers ftdi uninstall +### apio drivers list serial ``` -Usage: apio drivers ftdi uninstall [OPTIONS] +Usage: apio drivers list serial [OPTIONS] - The command 'apio drivers ftdi uninstall' removes the FTDI drivers that may - have been installed earlier. + The command ‘apio drivers list serial’ lists the serial devices connected to + your computer. It is useful for diagnosing FPGA board connectivity issues. Examples: - apio drivers ftdi install # Install the ftdi drivers. - apio drivers ftdi uinstall # Uinstall the ftdi drivers. + apio drivers list serial # List the serial devices. + + [Hint] This command executes the utility lsserial, which can also be invoked + using the command 'apio raw -- lsserial '. Options: -h, --help Show this message and exit. @@ -293,17 +331,17 @@ Options:
-### apio drivers lsusb +### apio drivers list usb ``` -Usage: apio drivers lsusb [OPTIONS] +Usage: apio drivers list usb [OPTIONS] - The command ‘apio drivers lsusb’ runs the lsusb utility to list the USB + The command ‘apio drivers list usb runs the lsusb utility to list the USB devices connected to your computer. It is typically used for diagnosing connectivity issues with FPGA boards. Examples: - apio drivers lsusb # List the usb devices + apio drivers list usb # List the usb devices [Hint] You can also run the lsusb utility using the command 'apio raw -- lsusb '. @@ -314,57 +352,35 @@ Options:
-### apio drivers serial +### apio drivers uninstall ``` -Usage: apio drivers serial [OPTIONS] COMMAND [ARGS]... +Usage: apio drivers uninstall [OPTIONS] COMMAND [ARGS]... - The command group 'apio drivers serial' includes subcommands designed to - manage and configure the serial drivers on your system. + The command group 'apio drivers uninstall' includes subcommands that that + uninstall system drivers that are used to upload designs to FPGA boards. Options: -h, --help Show this message and exit. Subcommands: - apio drivers serial install Install the serial drivers. - apio drivers serial uninstall Uninstall the serial drivers. - apio drivers serial list List the connected serial devices. - -``` - -
- -### apio drivers serial install - -``` -Usage: apio drivers serial install [OPTIONS] - - The command ‘apio drivers serial install’ installs the necessary serial - drivers on your system, as required by certain FPGA boards. - - Examples: - apio drivers serial install # Install the ftdi drivers. - apio drivers serial uinstall # Uinstall the ftdi drivers. + apio drivers uninstall ftdi Uninstall the ftdi drivers. + apio drivers uninstall serial Uninstall the serial drivers. -Options: - -h, --help Show this message and exit. ```
-### apio drivers serial list +### apio drivers uninstall ftdi ``` -Usage: apio drivers serial list [OPTIONS] +Usage: apio drivers uninstall ftdi [OPTIONS] - The command ‘apio drivers serial list’ lists the serial devices connected to - your computer. It is useful for diagnosing FPGA board connectivity issues. + The command 'apio drivers uninstall ftdi' removes the FTDI drivers that may + have been installed earlier. Examples: - apio drivers serial list # List the serial devices. - - [Hint] This command executes the utility lsserial, which can also be invoked - using the command 'apio raw -- lsserial '. + apio drivers uninstall ftdi # Uninstall the ftdi drivers. Options: -h, --help Show this message and exit. @@ -372,17 +388,16 @@ Options:
-### apio drivers serial uninstall +### apio drivers uninstall serial ``` -Usage: apio drivers serial uninstall [OPTIONS] +Usage: apio drivers uninstall serial [OPTIONS] - The command ‘apio drivers serial uninstall’ removes the serial drivers that + The command ‘apio drivers uninstall serial’ removes the serial drivers that you may have installed earlier. Examples: - apio drivers serial install # Install the serial drivers. - apio drivers serial uinstall # Uinstall the serial drivers. + apio drivers uinstall serial # Uinstall the serial drivers. Options: -h, --help Show this message and exit. @@ -467,13 +482,15 @@ Usage: apio examples list [OPTIONS] Examples: apio examples list # List all examples + apio examples list -v # List with extra information. apio examples list | grep alhambra-ii # Show examples of a specific board. apio examples list | grep -i blink # Show all blinking examples. Options: - -h, --help Show this message and exit. + -v, --verbose Show detailed output. + -h, --help Show this message and exit. ```
diff --git a/apio/commands/apio_drivers.py b/apio/commands/apio_drivers.py index 1e390bbb..4732e0db 100644 --- a/apio/commands/apio_drivers.py +++ b/apio/commands/apio_drivers.py @@ -7,46 +7,13 @@ # -- Licence GPLv2 """Implementation of 'apio drivers' command group.""" -import sys import click from apio.utils.cmd_util import ApioGroup, ApioSubgroup -from apio.commands import apio_drivers_ftdi, apio_drivers_serial -from apio.apio_context import ApioContext, ApioContextScope -from apio.managers.system import System - -# --- apio drivers lsusb - -APIO_DRIVERS_LSUSB_HELP = """ -The command ‘apio drivers lsusb’ runs the lsusb utility to list the USB -devices connected to your computer. It is typically used for diagnosing -connectivity issues with FPGA boards. - -\b -Examples: - apio drivers lsusb # List the usb devices - -[Hint] You can also run the lsusb utility using the command -'apio raw -- lsusb '. -""" - - -@click.command( - name="lsusb", - short_help="List connected USB devices.", - help=APIO_DRIVERS_LSUSB_HELP, +from apio.commands import ( + apio_drivers_list, + apio_drivers_install, + apio_drivers_uninstall, ) -def _lsusb_cli(): - """Implements the 'apio driverss lsusb' command.""" - - # Create the apio context. - apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - - # -- Create the system object - system = System(apio_ctx) - - # -- List the USB device. - exit_code = system.lsusb() - sys.exit(exit_code) # --- apio drivers @@ -61,9 +28,9 @@ def _lsusb_cli(): ApioSubgroup( "Subcommands", [ - apio_drivers_ftdi.cli, - apio_drivers_serial.cli, - _lsusb_cli, + apio_drivers_list.cli, + apio_drivers_install.cli, + apio_drivers_uninstall.cli, ], ) ] diff --git a/apio/commands/apio_drivers_ftdi.py b/apio/commands/apio_drivers_ftdi.py deleted file mode 100644 index f829efe4..00000000 --- a/apio/commands/apio_drivers_ftdi.py +++ /dev/null @@ -1,150 +0,0 @@ -# -*- coding: utf-8 -*- -# -- This file is part of the Apio project -# -- (C) 2016-2024 FPGAwars -# -- Authors -# -- * Jesús Arroyo (2016-2019) -# -- * Juan Gonzalez (obijuan) (2019-2024) -# -- Licence GPLv2 -"""Implementation of 'apio drivers ftdi' command""" - -import sys -import click -from apio.managers.drivers import Drivers -from apio.apio_context import ApioContext, ApioContextScope -from apio.utils.cmd_util import ApioGroup, ApioSubgroup -from apio.managers.system import System - - -# -- apio drivers ftdi install - -APIO_DRIVERS_FTDI_INSTALL_HELP = """ -The command 'apio drivers ftdi install' installs on your system the FTDI -drivers required by some FPGA boards. - -\b -Examples: - apio drivers ftdi install # Install the ftdi drivers. - apio drivers ftdi uinstall # Uinstall the ftdi drivers. -""" - - -@click.command( - name="install", - short_help="Install the ftdi drivers.", - help=APIO_DRIVERS_FTDI_INSTALL_HELP, -) -def _install_cli(): - """Implements the 'apio drivers ftdi install' command.""" - - # -- Create the apio context. - apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - - # # -- Create the drivers manager. - drivers = Drivers(apio_ctx) - - # # -- FTDI install option - # if ftdi_install: - exit_code = drivers.ftdi_install() - sys.exit(exit_code) - - -# -- apio driver ftdi uninstall - -APIO_DRIVERS_FTDI_UNINSTALL_HELP = """ -The command 'apio drivers ftdi uninstall' removes the FTDI drivers that may -have been installed earlier. - -\b -Examples: - apio drivers ftdi install # Install the ftdi drivers. - apio drivers ftdi uinstall # Uinstall the ftdi drivers. - -""" - - -@click.command( - name="uninstall", - short_help="Uninstall the ftdi drivers.", - help=APIO_DRIVERS_FTDI_UNINSTALL_HELP, -) -def _uninstall_cli(): - """Implements the 'apio drivers ftdi uninstall' command.""" - - # -- Create the apio context. - apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - - # -- Create the drivers manager. - drivers = Drivers(apio_ctx) - - # -- Uninstall - exit_code = drivers.ftdi_uninstall() - sys.exit(exit_code) - - -# -- apio drivers ftdi list - - -APIO_DRIVERS_FTDI_LIST_HELP = """ -The command 'apio drivers ftdi list' displays the FTDI devices currently -connected to your computer. It is useful for diagnosing FPGA board -connectivity issues. - -\b -Examples: - apio drivers ftdi list # List the ftdi devices. - -[Hint] This command uses the lsftdi utility, which can also be invoked -directly with the 'apio raw -- lsftdi ' command. -""" - - -@click.command( - name="list", - short_help="List the connected ftdi devices.", - help=APIO_DRIVERS_FTDI_LIST_HELP, -) -def _list_cli(): - """Implements the 'apio drivers ftdi list' command.""" - - # Create the apio context. - apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - - # -- Create the system object - system = System(apio_ctx) - - # -- List all connected ftdi devices - exit_code = system.lsftdi() - sys.exit(exit_code) - - -# --- apio drivers ftdi - -APIO_DRIVERS_FTDI_HELP = """ -The command group 'apio drivers ftdi' includes subcommands that help manage -the FTDI drivers on your system. -""" - -# -- We have only a single group with the title 'Subcommands'. -SUBGROUPS = [ - ApioSubgroup( - "Subcommands", - [ - _install_cli, - _uninstall_cli, - _list_cli, - ], - ) -] - - -@click.command( - name="ftdi", - cls=ApioGroup, - subgroups=SUBGROUPS, - short_help="Manage the ftdi drivers.", - help=APIO_DRIVERS_FTDI_HELP, -) -def cli(): - """Implements the 'apio drivers ftdi' command.""" - - # pass diff --git a/apio/commands/apio_drivers_install.py b/apio/commands/apio_drivers_install.py new file mode 100644 index 00000000..02acf799 --- /dev/null +++ b/apio/commands/apio_drivers_install.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# -- This file is part of the Apio project +# -- (C) 2016-2024 FPGAwars +# -- Authors +# -- * Jesús Arroyo (2016-2019) +# -- * Juan Gonzalez (obijuan) (2019-2024) +# -- Licence GPLv2 +"""Implementation of 'apio drivers install' command""" + +import sys +import click +from apio.managers.drivers import Drivers +from apio.apio_context import ApioContext, ApioContextScope +from apio.utils.cmd_util import ApioGroup, ApioSubgroup + + +# -- apio drivers install ftdi + +APIO_DRIVERS_INSTALL_FTDI_HELP = """ +The command 'apio drivers install ftdi' installs on your system the FTDI +drivers required by some FPGA boards. + +\b +Examples: + apio drivers install ftdi # Install the ftdi drivers. +""" + + +@click.command( + name="ftdi", + short_help="Install the ftdi drivers.", + help=APIO_DRIVERS_INSTALL_FTDI_HELP, +) +def _ftdi_cli(): + """Implements the 'apio drivers install ftdi' command.""" + + # -- Create the apio context. + apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) + + # # -- Create the drivers manager. + drivers = Drivers(apio_ctx) + + # -- Install. + exit_code = drivers.ftdi_install() + sys.exit(exit_code) + + +# -- apio driver install serial + +APIO_DRIVERS_INSTALL_SERIAL_HELP = """ +The command ‘apio drivers install serial’ installs the necessary serial +drivers on your system, as required by certain FPGA boards. + +\b +Examples: + apio drivers install serial # Install the serial drivers. +""" + + +@click.command( + name="serial", + short_help="Install the serial drivers.", + help=APIO_DRIVERS_INSTALL_SERIAL_HELP, +) +def _serial_cli(): + """Implements the 'apio drivers install serial' command.""" + + # -- Create the apio context. + apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) + + # -- Create the drivers manager. + drivers = Drivers(apio_ctx) + + # Insall + exit_code = drivers.serial_install() + sys.exit(exit_code) + + +# --- apio drivers install + +APIO_DRIVERS_INSTALL_HELP = """ +The command group 'apio drivers install' includes subcommands that that +install system drivers that are used to upload designs to FPGA boards. +""" + +# -- We have only a single group with the title 'Subcommands'. +SUBGROUPS = [ + ApioSubgroup( + "Subcommands", + [ + _ftdi_cli, + _serial_cli, + ], + ) +] + + +@click.command( + name="install", + cls=ApioGroup, + subgroups=SUBGROUPS, + short_help="Install drivers.", + help=APIO_DRIVERS_INSTALL_HELP, +) +def cli(): + """Implements the 'apio drivers install' command.""" + + # pass diff --git a/apio/commands/apio_drivers_list.py b/apio/commands/apio_drivers_list.py new file mode 100644 index 00000000..01995d7a --- /dev/null +++ b/apio/commands/apio_drivers_list.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +# -- This file is part of the Apio project +# -- (C) 2016-2024 FPGAwars +# -- Authors +# -- * Jesús Arroyo (2016-2019) +# -- * Juan Gonzalez (obijuan) (2019-2024) +# -- Licence GPLv2 +"""Implementation of 'apio drivers list' command""" + +import sys +import click +from apio.apio_context import ApioContext, ApioContextScope +from apio.utils.cmd_util import ApioGroup, ApioSubgroup +from apio.managers.system import System + + +# -- apio drivers list ftdi + + +APIO_DRIVERS_LIST_FTDI_HELP = """ +The command 'apio drivers list ftdi' displays the FTDI devices currently +connected to your computer. It is useful for diagnosing FPGA board +connectivity issues. + +\b +Examples: + apio drivers list ftdi # List the ftdi devices. + +[Hint] This command uses the lsftdi utility, which can also be invoked +directly with the 'apio raw -- lsftdi ' command. +""" + + +@click.command( + name="ftdi", + short_help="List the connected ftdi devices.", + help=APIO_DRIVERS_LIST_FTDI_HELP, +) +def _ftdi_cli(): + """Implements the 'apio drivers list ftdi' command.""" + + # Create the apio context. + apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) + + # -- Create the system object + system = System(apio_ctx) + + # -- List all connected ftdi devices + exit_code = system.lsftdi() + sys.exit(exit_code) + + +# -- apio drivers list serial + +APIO_DRIVERS_LIST_SERIAL_HELP = """ +The command ‘apio drivers list serial’ lists the serial devices connected to +your computer. It is useful for diagnosing FPGA board connectivity issues. + +\b +Examples: + apio drivers list serial # List the serial devices. + +[Hint] This command executes the utility lsserial, which can also be invoked +using the command 'apio raw -- lsserial '. +""" + + +@click.command( + name="serial", + short_help="List the connected serial devices.", + help=APIO_DRIVERS_LIST_SERIAL_HELP, +) +def _serial_cli(): + """Implements the 'apio drivers list serial' command.""" + + # Create the apio context. + apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) + + # -- Create the system object + system = System(apio_ctx) + + # # -- List all connected serial devices + exit_code = system.lsserial() + sys.exit(exit_code) + + +# --- apio drivers list usb + +APIO_DRIVERS_LIST_USB_HELP = """ +The command ‘apio drivers list usb runs the lsusb utility to list the USB +devices connected to your computer. It is typically used for diagnosing +connectivity issues with FPGA boards. + +\b +Examples: + apio drivers list usb # List the usb devices + +[Hint] You can also run the lsusb utility using the command +'apio raw -- lsusb '. +""" + + +@click.command( + name="usb", + short_help="List connected USB devices.", + help=APIO_DRIVERS_LIST_USB_HELP, +) +def _usb_cli(): + """Implements the 'apio driverss list usb' command.""" + + # Create the apio context. + apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) + + # -- Create the system object + system = System(apio_ctx) + + # -- List the USB device. + exit_code = system.lsusb() + sys.exit(exit_code) + + +# --- apio drivers list + +APIO_DRIVERS_LIST_HELP = """ +The command group 'apio drivers list' includes subcommands that that lists +system drivers that are used with FPGA boards. +""" + +# -- We have only a single group with the title 'Subcommands'. +SUBGROUPS = [ + ApioSubgroup( + "Subcommands", + [ + _ftdi_cli, + _serial_cli, + _usb_cli, + ], + ) +] + + +@click.command( + name="list", + cls=ApioGroup, + subgroups=SUBGROUPS, + short_help="List system drivers.", + help=APIO_DRIVERS_LIST_HELP, +) +def cli(): + """Implements the 'apio drivers list' command.""" + + # pass diff --git a/apio/commands/apio_drivers_serial.py b/apio/commands/apio_drivers_serial.py deleted file mode 100644 index 280fc6ae..00000000 --- a/apio/commands/apio_drivers_serial.py +++ /dev/null @@ -1,147 +0,0 @@ -# -*- coding: utf-8 -*- -# -- This file is part of the Apio project -# -- (C) 2016-2024 FPGAwars -# -- Authors -# -- * Jesús Arroyo (2016-2019) -# -- * Juan Gonzalez (obijuan) (2019-2024) -# -- Licence GPLv2 -"""Implementation of 'apio drivers serial' command""" - -import sys -import click -from apio.managers.drivers import Drivers -from apio.managers.system import System -from apio.apio_context import ApioContext, ApioContextScope -from apio.utils.cmd_util import ApioGroup, ApioSubgroup - - -# -- apio driver serial install - -APIO_DRIVERS_SERIAL_INSTALL_HELP = """ -The command ‘apio drivers serial install’ installs the necessary serial -drivers on your system, as required by certain FPGA boards. - -\b -Examples: - apio drivers serial install # Install the ftdi drivers. - apio drivers serial uinstall # Uinstall the ftdi drivers. -""" - - -@click.command( - name="install", - short_help="Install the serial drivers.", - help=APIO_DRIVERS_SERIAL_INSTALL_HELP, -) -def _install_cli(): - """Implements the 'apio drivers serial install' command.""" - - # -- Create the apio context. - apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - - # -- Create the drivers manager. - drivers = Drivers(apio_ctx) - - # Insall - exit_code = drivers.serial_install() - sys.exit(exit_code) - - -# -- apio drivers serial uninstall - -APIO_DRIVERS_SERIAL_UNINSTALL_HELP = """ -The command ‘apio drivers serial uninstall’ removes the serial drivers that -you may have installed earlier. - -\b -Examples: - apio drivers serial install # Install the serial drivers. - apio drivers serial uinstall # Uinstall the serial drivers. - -""" - - -@click.command( - name="uninstall", - short_help="Uninstall the serial drivers.", - help=APIO_DRIVERS_SERIAL_UNINSTALL_HELP, -) -def _uninstall_cli(): - """Implements the 'apio drivers serial uninstall' command.""" - - # -- Create the apio context. - apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - - # -- Create the drivers manager. - drivers = Drivers(apio_ctx) - - # -- Uninstall - exit_code = drivers.serial_uninstall() - sys.exit(exit_code) - - -# -- apio drivers serial list - -APIO_DRIVERS_SERIAL_LIST_HELP = """ -The command ‘apio drivers serial list’ lists the serial devices connected to -your computer. It is useful for diagnosing FPGA board connectivity issues. - -\b -Examples: - apio drivers serial list # List the serial devices. - -[Hint] This command executes the utility lsserial, which can also be invoked -using the command 'apio raw -- lsserial '. -""" - - -@click.command( - name="list", - short_help="List the connected serial devices.", - help=APIO_DRIVERS_SERIAL_LIST_HELP, -) -def _list_cli(): - """Implements the 'apio drivers serial list' command.""" - - # Create the apio context. - apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) - - # -- Create the system object - system = System(apio_ctx) - - # # -- List all connected serial devices - exit_code = system.lsserial() - sys.exit(exit_code) - - -# --- apio drivers serial - -APIO_DRIVERS_SERIAL_HELP = """ -The command group 'apio drivers serial' includes subcommands designed to -manage and configure the serial drivers on your system. -""" - -# -- We have only a single group with the title 'Subcommands'. -SUBGROUPS = [ - ApioSubgroup( - "Subcommands", - [ - _install_cli, - _uninstall_cli, - _list_cli, - ], - ) -] - - -@click.command( - name="serial", - cls=ApioGroup, - subgroups=SUBGROUPS, - short_help="Manage the serial drivers.", - help=APIO_DRIVERS_SERIAL_HELP, -) -def cli(): - """Implements the 'apio drivers serial' command.""" - - # pass diff --git a/apio/commands/apio_drivers_uninstall.py b/apio/commands/apio_drivers_uninstall.py new file mode 100644 index 00000000..19624203 --- /dev/null +++ b/apio/commands/apio_drivers_uninstall.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# -- This file is part of the Apio project +# -- (C) 2016-2024 FPGAwars +# -- Authors +# -- * Jesús Arroyo (2016-2019) +# -- * Juan Gonzalez (obijuan) (2019-2024) +# -- Licence GPLv2 +"""Implementation of 'apio drivers uninstall' command""" + +import sys +import click +from apio.managers.drivers import Drivers +from apio.apio_context import ApioContext, ApioContextScope +from apio.utils.cmd_util import ApioGroup, ApioSubgroup + + +# -- apio driver uninstall ftdi + +APIO_DRIVERS_UNINSTALL_FTDI_HELP = """ +The command 'apio drivers uninstall ftdi' removes the FTDI drivers that may +have been installed earlier. + +\b +Examples: + apio drivers uninstall ftdi # Uninstall the ftdi drivers. + +""" + + +@click.command( + name="ftdi", + short_help="Uninstall the ftdi drivers.", + help=APIO_DRIVERS_UNINSTALL_FTDI_HELP, +) +def _ftdi_cli(): + """Implements the 'apio drivers uninstall ftdi' command.""" + + # -- Create the apio context. + apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) + + # -- Create the drivers manager. + drivers = Drivers(apio_ctx) + + # -- Uninstall + exit_code = drivers.ftdi_uninstall() + sys.exit(exit_code) + + +# -- apio drivers uninstall serial + +APIO_DRIVERS_UNINSTALL_SERIAL_HELP = """ +The command ‘apio drivers uninstall serial’ removes the serial drivers that +you may have installed earlier. + +\b +Examples: + apio drivers uinstall serial # Uinstall the serial drivers. +""" + + +@click.command( + name="serial", + short_help="Uninstall the serial drivers.", + help=APIO_DRIVERS_UNINSTALL_SERIAL_HELP, +) +def _serial_cli(): + """Implements the 'apio drivers uninstall serial' command.""" + + # -- Create the apio context. + apio_ctx = ApioContext(scope=ApioContextScope.NO_PROJECT) + + # -- Create the drivers manager. + drivers = Drivers(apio_ctx) + + # -- Uninstall + exit_code = drivers.serial_uninstall() + sys.exit(exit_code) + + +# --- apio drivers uninstall + +APIO_DRIVERS_UNINSTALL_HELP = """ +The command group 'apio drivers uninstall' includes subcommands that that +uninstall system drivers that are used to upload designs to FPGA boards. +""" + +# -- We have only a single group with the title 'Subcommands'. +SUBGROUPS = [ + ApioSubgroup( + "Subcommands", + [ + _ftdi_cli, + _serial_cli, + ], + ) +] + + +@click.command( + name="uninstall", + cls=ApioGroup, + subgroups=SUBGROUPS, + short_help="Uninstall drivers.", + help=APIO_DRIVERS_UNINSTALL_HELP, +) +def cli(): + """Implements the 'apio drivers uninstall' command.""" + + # pass From eab35df82d681fe4e854646274f4ebbbb3fcb173 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 14:18:22 -0800 Subject: [PATCH 06/12] A left over from the previous commit. --- test/commands/test_apio_drivers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/commands/test_apio_drivers.py b/test/commands/test_apio_drivers.py index deabaeb0..45033294 100644 --- a/test/commands/test_apio_drivers.py +++ b/test/commands/test_apio_drivers.py @@ -15,6 +15,6 @@ def test_drivers(apio_runner: ApioRunner): # -- Execute "apio drivers" result = sb.invoke_apio_cmd(apio, "drivers") sb.assert_ok(result) - assert "apio drivers ftdi" in unstyle(result.output) - assert "apio drivers serial" in unstyle(result.output) - assert "apio drivers lsusb" in unstyle(result.output) + assert "apio drivers list" in unstyle(result.output) + assert "apio drivers install" in unstyle(result.output) + assert "apio drivers uninstall" in unstyle(result.output) From 99fdaba5407c90c4ffc2d488c4cdd7643d6f2634 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 14:19:36 -0800 Subject: [PATCH 07/12] Added apio.ini option yosys-synth-extra-options per the discussion here Added apio.ini option yosys-synth-extra-options per the discussion here --- apio/managers/project.py | 2 + apio/managers/scons.py | 6 ++- apio/proto/apio.proto | 2 + apio/proto/apio_pb2.py | 40 +++++++-------- apio/proto/apio_pb2.pyi | 6 ++- apio/scons/plugin_ecp5.py | 7 ++- apio/scons/plugin_gowin.py | 4 +- apio/scons/plugin_ice40.py | 7 ++- .../alhambra-ii/icestudio-ledon/apio.ini | 3 ++ test/managers/test_project.py | 50 ++++++++++++++++++- 10 files changed, 97 insertions(+), 30 deletions(-) diff --git a/apio/managers/project.py b/apio/managers/project.py index ffb7e6e8..b03503e4 100644 --- a/apio/managers/project.py +++ b/apio/managers/project.py @@ -42,6 +42,8 @@ "default-testbench", # -- Multi line list of verible options for 'apio format' "format-verible-options", + # -- Additional option for the yosys synth command (inside the -p arg). + "yosys-synth-extra-options", } # -- Set of all options a project may have. diff --git a/apio/managers/scons.py b/apio/managers/scons.py index 594a107a..3b36f69a 100644 --- a/apio/managers/scons.py +++ b/apio/managers/scons.py @@ -318,7 +318,11 @@ def construct_scons_params( # -- Populate the Project params. result.project.MergeFrom( Project( - board_id=project["board"], top_module=project["top-module"] + board_id=project["board"], + top_module=project["top-module"], + yosys_synth_extra_options=apio_ctx.project.get( + "yosys-synth-extra-options", None + ), ) ) assert result.project.IsInitialized(), result diff --git a/apio/proto/apio.proto b/apio/proto/apio.proto index 4c845299..baf2f82b 100644 --- a/apio/proto/apio.proto +++ b/apio/proto/apio.proto @@ -83,6 +83,8 @@ message Envrionment { message Project { required string board_id = 1; required string top_module = 2; + // The value of 'yosys-synth-extra-options' from apio.ini. + optional string yosys_synth_extra_options = 3 [default = ""]; } // Lint target specific params. diff --git a/apio/proto/apio_pb2.py b/apio/proto/apio_pb2.py index e71ccec4..c225d520 100644 --- a/apio/proto/apio_pb2.py +++ b/apio/proto/apio_pb2.py @@ -30,17 +30,17 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\napio.proto\x12\napio.proto\"+\n\rIce40FpgaInfo\x12\x0c\n\x04type\x18\x01 \x02(\t\x12\x0c\n\x04pack\x18\x02 \x02(\t\"9\n\x0c\x45\x63p5FpgaInfo\x12\x0c\n\x04type\x18\x04 \x02(\t\x12\x0c\n\x04pack\x18\x05 \x02(\t\x12\r\n\x05speed\x18\x06 \x02(\t\"\x1f\n\rGowinFpgaInfo\x12\x0e\n\x06\x66\x61mily\x18\x04 \x02(\t\"\xc5\x01\n\x08\x46pgaInfo\x12\x0f\n\x07\x66pga_id\x18\x01 \x02(\t\x12\x10\n\x08part_num\x18\x02 \x02(\t\x12\x0c\n\x04size\x18\x03 \x02(\t\x12*\n\x05ice40\x18\n \x01(\x0b\x32\x19.apio.proto.Ice40FpgaInfoH\x00\x12(\n\x04\x65\x63p5\x18\x0b \x01(\x0b\x32\x18.apio.proto.Ecp5FpgaInfoH\x00\x12*\n\x05gowin\x18\x0c \x01(\x0b\x32\x19.apio.proto.GowinFpgaInfoH\x00\x42\x06\n\x04\x61rch\"I\n\tVerbosity\x12\x12\n\x03\x61ll\x18\x01 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x05synth\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x12\n\x03pnr\x18\x03 \x01(\x08:\x05\x66\x61lse\"e\n\x0b\x45nvrionment\x12\x13\n\x0bplatform_id\x18\x01 \x02(\t\x12\x17\n\x08is_debug\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x12\n\nyosys_path\x18\x03 \x02(\t\x12\x14\n\x0ctrellis_path\x18\x04 \x02(\t\"/\n\x07Project\x12\x10\n\x08\x62oard_id\x18\x01 \x02(\t\x12\x12\n\ntop_module\x18\x02 \x02(\t\"\x98\x01\n\nLintParams\x12\x14\n\ntop_module\x18\x01 \x01(\t:\x00\x12\x1c\n\rverilator_all\x18\x02 \x01(\x08:\x05\x66\x61lse\x12!\n\x12verilator_no_style\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x12verilator_no_warns\x18\x04 \x03(\t\x12\x17\n\x0fverilator_warns\x18\x05 \x03(\t\"S\n\x0bGraphParams\x12\x30\n\x0boutput_type\x18\x01 \x02(\x0e\x32\x1b.apio.proto.GraphOutputType\x12\x12\n\ntop_module\x18\x02 \x01(\t\"3\n\tSimParams\x12\x13\n\ttestbench\x18\x01 \x01(\t:\x00\x12\x11\n\tforce_sim\x18\x02 \x02(\x08\"%\n\x0e\x41pioTestParams\x12\x13\n\ttestbench\x18\x01 \x01(\t:\x00\"&\n\x0cUploadParams\x12\x16\n\x0eprogrammer_cmd\x18\x01 \x01(\t\"\xe8\x01\n\x0cTargetParams\x12&\n\x04lint\x18\n \x01(\x0b\x32\x16.apio.proto.LintParamsH\x00\x12(\n\x05graph\x18\x0b \x01(\x0b\x32\x17.apio.proto.GraphParamsH\x00\x12$\n\x03sim\x18\x0c \x01(\x0b\x32\x15.apio.proto.SimParamsH\x00\x12*\n\x04test\x18\r \x01(\x0b\x32\x1a.apio.proto.ApioTestParamsH\x00\x12*\n\x06upload\x18\x0e \x01(\x0b\x32\x18.apio.proto.UploadParamsH\x00\x42\x08\n\x06target\"\x95\x02\n\x0bSconsParams\x12\x11\n\ttimestamp\x18\x01 \x02(\t\x12\"\n\x04\x61rch\x18\x02 \x02(\x0e\x32\x14.apio.proto.ApioArch\x12\'\n\tfpga_info\x18\x03 \x02(\x0b\x32\x14.apio.proto.FpgaInfo\x12(\n\tverbosity\x18\x04 \x01(\x0b\x32\x15.apio.proto.Verbosity\x12,\n\x0b\x65nvrionment\x18\x05 \x02(\x0b\x32\x17.apio.proto.Envrionment\x12$\n\x07project\x18\x06 \x02(\x0b\x32\x13.apio.proto.Project\x12(\n\x06target\x18\x07 \x01(\x0b\x32\x18.apio.proto.TargetParams*@\n\x08\x41pioArch\x12\x14\n\x10\x41RCH_UNSPECIFIED\x10\x00\x12\t\n\x05ICE40\x10\x01\x12\x08\n\x04\x45\x43P5\x10\x02\x12\t\n\x05GOWIN\x10\x03*B\n\x0fGraphOutputType\x12\x14\n\x10TYPE_UNSPECIFIED\x10\x00\x12\x07\n\x03SVG\x10\x01\x12\x07\n\x03PNG\x10\x02\x12\x07\n\x03PDF\x10\x03') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\napio.proto\x12\napio.proto\"+\n\rIce40FpgaInfo\x12\x0c\n\x04type\x18\x01 \x02(\t\x12\x0c\n\x04pack\x18\x02 \x02(\t\"9\n\x0c\x45\x63p5FpgaInfo\x12\x0c\n\x04type\x18\x04 \x02(\t\x12\x0c\n\x04pack\x18\x05 \x02(\t\x12\r\n\x05speed\x18\x06 \x02(\t\"\x1f\n\rGowinFpgaInfo\x12\x0e\n\x06\x66\x61mily\x18\x04 \x02(\t\"\xc5\x01\n\x08\x46pgaInfo\x12\x0f\n\x07\x66pga_id\x18\x01 \x02(\t\x12\x10\n\x08part_num\x18\x02 \x02(\t\x12\x0c\n\x04size\x18\x03 \x02(\t\x12*\n\x05ice40\x18\n \x01(\x0b\x32\x19.apio.proto.Ice40FpgaInfoH\x00\x12(\n\x04\x65\x63p5\x18\x0b \x01(\x0b\x32\x18.apio.proto.Ecp5FpgaInfoH\x00\x12*\n\x05gowin\x18\x0c \x01(\x0b\x32\x19.apio.proto.GowinFpgaInfoH\x00\x42\x06\n\x04\x61rch\"I\n\tVerbosity\x12\x12\n\x03\x61ll\x18\x01 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x05synth\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x12\n\x03pnr\x18\x03 \x01(\x08:\x05\x66\x61lse\"e\n\x0b\x45nvrionment\x12\x13\n\x0bplatform_id\x18\x01 \x02(\t\x12\x17\n\x08is_debug\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x12\n\nyosys_path\x18\x03 \x02(\t\x12\x14\n\x0ctrellis_path\x18\x04 \x02(\t\"T\n\x07Project\x12\x10\n\x08\x62oard_id\x18\x01 \x02(\t\x12\x12\n\ntop_module\x18\x02 \x02(\t\x12#\n\x19yosys_synth_extra_options\x18\x03 \x01(\t:\x00\"\x98\x01\n\nLintParams\x12\x14\n\ntop_module\x18\x01 \x01(\t:\x00\x12\x1c\n\rverilator_all\x18\x02 \x01(\x08:\x05\x66\x61lse\x12!\n\x12verilator_no_style\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x12verilator_no_warns\x18\x04 \x03(\t\x12\x17\n\x0fverilator_warns\x18\x05 \x03(\t\"S\n\x0bGraphParams\x12\x30\n\x0boutput_type\x18\x01 \x02(\x0e\x32\x1b.apio.proto.GraphOutputType\x12\x12\n\ntop_module\x18\x02 \x01(\t\"3\n\tSimParams\x12\x13\n\ttestbench\x18\x01 \x01(\t:\x00\x12\x11\n\tforce_sim\x18\x02 \x02(\x08\"%\n\x0e\x41pioTestParams\x12\x13\n\ttestbench\x18\x01 \x01(\t:\x00\"&\n\x0cUploadParams\x12\x16\n\x0eprogrammer_cmd\x18\x01 \x01(\t\"\xe8\x01\n\x0cTargetParams\x12&\n\x04lint\x18\n \x01(\x0b\x32\x16.apio.proto.LintParamsH\x00\x12(\n\x05graph\x18\x0b \x01(\x0b\x32\x17.apio.proto.GraphParamsH\x00\x12$\n\x03sim\x18\x0c \x01(\x0b\x32\x15.apio.proto.SimParamsH\x00\x12*\n\x04test\x18\r \x01(\x0b\x32\x1a.apio.proto.ApioTestParamsH\x00\x12*\n\x06upload\x18\x0e \x01(\x0b\x32\x18.apio.proto.UploadParamsH\x00\x42\x08\n\x06target\"\x95\x02\n\x0bSconsParams\x12\x11\n\ttimestamp\x18\x01 \x02(\t\x12\"\n\x04\x61rch\x18\x02 \x02(\x0e\x32\x14.apio.proto.ApioArch\x12\'\n\tfpga_info\x18\x03 \x02(\x0b\x32\x14.apio.proto.FpgaInfo\x12(\n\tverbosity\x18\x04 \x01(\x0b\x32\x15.apio.proto.Verbosity\x12,\n\x0b\x65nvrionment\x18\x05 \x02(\x0b\x32\x17.apio.proto.Envrionment\x12$\n\x07project\x18\x06 \x02(\x0b\x32\x13.apio.proto.Project\x12(\n\x06target\x18\x07 \x01(\x0b\x32\x18.apio.proto.TargetParams*@\n\x08\x41pioArch\x12\x14\n\x10\x41RCH_UNSPECIFIED\x10\x00\x12\t\n\x05ICE40\x10\x01\x12\x08\n\x04\x45\x43P5\x10\x02\x12\t\n\x05GOWIN\x10\x03*B\n\x0fGraphOutputType\x12\x14\n\x10TYPE_UNSPECIFIED\x10\x00\x12\x07\n\x03SVG\x10\x01\x12\x07\n\x03PNG\x10\x02\x12\x07\n\x03PDF\x10\x03') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'apio_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_APIOARCH']._serialized_start=1477 - _globals['_APIOARCH']._serialized_end=1541 - _globals['_GRAPHOUTPUTTYPE']._serialized_start=1543 - _globals['_GRAPHOUTPUTTYPE']._serialized_end=1609 + _globals['_APIOARCH']._serialized_start=1514 + _globals['_APIOARCH']._serialized_end=1578 + _globals['_GRAPHOUTPUTTYPE']._serialized_start=1580 + _globals['_GRAPHOUTPUTTYPE']._serialized_end=1646 _globals['_ICE40FPGAINFO']._serialized_start=26 _globals['_ICE40FPGAINFO']._serialized_end=69 _globals['_ECP5FPGAINFO']._serialized_start=71 @@ -54,19 +54,19 @@ _globals['_ENVRIONMENT']._serialized_start=438 _globals['_ENVRIONMENT']._serialized_end=539 _globals['_PROJECT']._serialized_start=541 - _globals['_PROJECT']._serialized_end=588 - _globals['_LINTPARAMS']._serialized_start=591 - _globals['_LINTPARAMS']._serialized_end=743 - _globals['_GRAPHPARAMS']._serialized_start=745 - _globals['_GRAPHPARAMS']._serialized_end=828 - _globals['_SIMPARAMS']._serialized_start=830 - _globals['_SIMPARAMS']._serialized_end=881 - _globals['_APIOTESTPARAMS']._serialized_start=883 - _globals['_APIOTESTPARAMS']._serialized_end=920 - _globals['_UPLOADPARAMS']._serialized_start=922 - _globals['_UPLOADPARAMS']._serialized_end=960 - _globals['_TARGETPARAMS']._serialized_start=963 - _globals['_TARGETPARAMS']._serialized_end=1195 - _globals['_SCONSPARAMS']._serialized_start=1198 - _globals['_SCONSPARAMS']._serialized_end=1475 + _globals['_PROJECT']._serialized_end=625 + _globals['_LINTPARAMS']._serialized_start=628 + _globals['_LINTPARAMS']._serialized_end=780 + _globals['_GRAPHPARAMS']._serialized_start=782 + _globals['_GRAPHPARAMS']._serialized_end=865 + _globals['_SIMPARAMS']._serialized_start=867 + _globals['_SIMPARAMS']._serialized_end=918 + _globals['_APIOTESTPARAMS']._serialized_start=920 + _globals['_APIOTESTPARAMS']._serialized_end=957 + _globals['_UPLOADPARAMS']._serialized_start=959 + _globals['_UPLOADPARAMS']._serialized_end=997 + _globals['_TARGETPARAMS']._serialized_start=1000 + _globals['_TARGETPARAMS']._serialized_end=1232 + _globals['_SCONSPARAMS']._serialized_start=1235 + _globals['_SCONSPARAMS']._serialized_end=1512 # @@protoc_insertion_point(module_scope) diff --git a/apio/proto/apio_pb2.pyi b/apio/proto/apio_pb2.pyi index 88dd5310..7ee9ee86 100644 --- a/apio/proto/apio_pb2.pyi +++ b/apio/proto/apio_pb2.pyi @@ -97,12 +97,14 @@ class Envrionment(_message.Message): def __init__(self, platform_id: _Optional[str] = ..., is_debug: bool = ..., yosys_path: _Optional[str] = ..., trellis_path: _Optional[str] = ...) -> None: ... class Project(_message.Message): - __slots__ = ("board_id", "top_module") + __slots__ = ("board_id", "top_module", "yosys_synth_extra_options") BOARD_ID_FIELD_NUMBER: _ClassVar[int] TOP_MODULE_FIELD_NUMBER: _ClassVar[int] + YOSYS_SYNTH_EXTRA_OPTIONS_FIELD_NUMBER: _ClassVar[int] board_id: str top_module: str - def __init__(self, board_id: _Optional[str] = ..., top_module: _Optional[str] = ...) -> None: ... + yosys_synth_extra_options: str + def __init__(self, board_id: _Optional[str] = ..., top_module: _Optional[str] = ..., yosys_synth_extra_options: _Optional[str] = ...) -> None: ... class LintParams(_message.Message): __slots__ = ("top_module", "verilator_all", "verilator_no_style", "verilator_no_warns", "verilator_warns") diff --git a/apio/scons/plugin_ecp5.py b/apio/scons/plugin_ecp5.py index 90a2c0be..2be68dc4 100644 --- a/apio/scons/plugin_ecp5.py +++ b/apio/scons/plugin_ecp5.py @@ -61,9 +61,12 @@ def synth_builder(self) -> BuilderBase: # -- The yosys synth builder. return Builder( - action='yosys -p "synth_ecp5 -top {0} -json $TARGET" {1} ' - "$SOURCES".format( + action=( + 'yosys -p "synth_ecp5 -top {0} {1} -json $TARGET" {2} ' + "$SOURCES" + ).format( params.project.top_module, + params.project.yosys_synth_extra_options, "" if params.verbosity.all or params.verbosity.synth else "-q", ), suffix=".json", diff --git a/apio/scons/plugin_gowin.py b/apio/scons/plugin_gowin.py index 6d53120b..8fca8ad8 100644 --- a/apio/scons/plugin_gowin.py +++ b/apio/scons/plugin_gowin.py @@ -59,9 +59,11 @@ def synth_builder(self) -> BuilderBase: # -- The yosys synth builder. return Builder( action=( - 'yosys -p "synth_gowin -top {0} -json $TARGET" {1} $SOURCES' + 'yosys -p "synth_gowin -top {0} {1} -json $TARGET" {2} ' + "$SOURCES" ).format( params.project.top_module, + params.project.yosys_synth_extra_options, "" if params.verbosity.all or params.verbosity.synth else "-q", ), suffix=".json", diff --git a/apio/scons/plugin_ice40.py b/apio/scons/plugin_ice40.py index f518ec5c..3fd8b073 100644 --- a/apio/scons/plugin_ice40.py +++ b/apio/scons/plugin_ice40.py @@ -58,9 +58,12 @@ def synth_builder(self) -> BuilderBase: # -- The yosys synth builder. return Builder( - action='yosys -p "synth_ice40 -top {0} -json $TARGET" {1} ' - "$SOURCES".format( + action=( + 'yosys -p "synth_ice40 -top {0} {1} -json $TARGET" {2} ' + "$SOURCES" + ).format( params.project.top_module, + params.project.yosys_synth_extra_options, "" if params.verbosity.all or params.verbosity.synth else "-q", ), suffix=".json", diff --git a/test-examples/ice40/alhambra-ii/icestudio-ledon/apio.ini b/test-examples/ice40/alhambra-ii/icestudio-ledon/apio.ini index 45e372f9..2efffce8 100644 --- a/test-examples/ice40/alhambra-ii/icestudio-ledon/apio.ini +++ b/test-examples/ice40/alhambra-ii/icestudio-ledon/apio.ini @@ -2,6 +2,9 @@ board = alhambra-ii top-module = main +# Inject into the yosys sync -p argument. +yosys-synth-extra-options = -dsp + # Style preferences for 'apio format'. format-verible-options = --column_limit=80 diff --git a/test/managers/test_project.py b/test/managers/test_project.py index 87ad92af..a70b2877 100644 --- a/test/managers/test_project.py +++ b/test/managers/test_project.py @@ -9,8 +9,8 @@ # TODO: Add more tests. -def test_options(apio_runner: ApioRunner): - """Tests the options access.""" +def test_required_and_optionals(apio_runner: ApioRunner): + """Tests the required and optional options.""" with apio_runner.in_sandbox() as sb: @@ -20,6 +20,7 @@ def test_options(apio_runner: ApioRunner): "board": "alhambra-ii", "top-module": "main", "format-verible-options": "\n --aaa bbb\n --ccc ddd", + "yosys-synth-extra-options": "-dsp -xyz", } ) @@ -30,12 +31,57 @@ def test_options(apio_runner: ApioRunner): ) project = apio_ctx.project + # -- Verify the required args. assert project.get("board") == "alhambra-ii" assert project.get("top-module") == "main" + + # -- Verify the optional args. assert project.get_as_lines_list("format-verible-options") == [ "--aaa bbb", "--ccc ddd", ] + assert project.get("yosys-synth-extra-options") == "-dsp -xyz" + + # -- Try a few as dict lookup. + assert project["board"] == "alhambra-ii" + assert project["top-module"] == "main" + + +def test_required_only(apio_runner: ApioRunner): + """Tests the required only options.""" + + with apio_runner.in_sandbox() as sb: + + # -- Create an apio.ini. + sb.write_apio_ini( + { + "board": "alhambra-ii", + "top-module": "main", + } + ) + + # -- We use ApioContext to instantiate the Project object. + apio_ctx = ApioContext( + scope=ApioContextScope.PROJECT_REQUIRED, + project_dir_arg=sb.proj_dir, + ) + project = apio_ctx.project + + # -- Verify the required args. + assert project.get("board") == "alhambra-ii" + assert project.get("top-module") == "main" + + # -- Verify the optional args + assert project.get_as_lines_list("format-verible-options") is None + assert project.get("yosys-synth-extra-options") is None + + # -- Verify optionals while specifying explicit defaults. + assert ( + project.get_as_lines_list("format-verible-options", default=[]) + == [] + ) + assert project.get("yosys-synth-extra-options", "") == "" + # -- Try a few as dict lookup. assert project["board"] == "alhambra-ii" assert project["top-module"] == "main" From 8dc8f439ae19a4f0d024285923f167b86437a111 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 14:32:47 -0800 Subject: [PATCH 08/12] Fixed the seperation to required and optional apio.ini option in a test. top-module moved to the optional category. No functional change. --- test/managers/test_project.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/managers/test_project.py b/test/managers/test_project.py index a70b2877..d5a8f3df 100644 --- a/test/managers/test_project.py +++ b/test/managers/test_project.py @@ -17,8 +17,10 @@ def test_required_and_optionals(apio_runner: ApioRunner): # -- Create an apio.ini. sb.write_apio_ini( { + # -- Requied. "board": "alhambra-ii", - "top-module": "main", + # -- Optional. + "top-module": "my_module", "format-verible-options": "\n --aaa bbb\n --ccc ddd", "yosys-synth-extra-options": "-dsp -xyz", } @@ -33,9 +35,9 @@ def test_required_and_optionals(apio_runner: ApioRunner): # -- Verify the required args. assert project.get("board") == "alhambra-ii" - assert project.get("top-module") == "main" # -- Verify the optional args. + assert project.get("top-module") == "my_module" assert project.get_as_lines_list("format-verible-options") == [ "--aaa bbb", "--ccc ddd", @@ -44,7 +46,7 @@ def test_required_and_optionals(apio_runner: ApioRunner): # -- Try a few as dict lookup. assert project["board"] == "alhambra-ii" - assert project["top-module"] == "main" + assert project["top-module"] == "my_module" def test_required_only(apio_runner: ApioRunner): @@ -56,7 +58,6 @@ def test_required_only(apio_runner: ApioRunner): sb.write_apio_ini( { "board": "alhambra-ii", - "top-module": "main", } ) @@ -69,9 +70,9 @@ def test_required_only(apio_runner: ApioRunner): # -- Verify the required args. assert project.get("board") == "alhambra-ii" - assert project.get("top-module") == "main" # -- Verify the optional args + assert project.get("top-module") == "main" assert project.get_as_lines_list("format-verible-options") is None assert project.get("yosys-synth-extra-options") is None From 599868d8267033036cfd1df6bc15910097045031 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 19:30:32 -0800 Subject: [PATCH 09/12] Refactored scons_handler.py to be more readable. Each scons target is now handled in a seperate method which registers that scons builders and targets that are relevant for that command. Should have no functional change. --- apio/scons/plugin_base.py | 1 + apio/scons/plugin_ecp5.py | 1 + apio/scons/plugin_gowin.py | 1 + apio/scons/plugin_ice40.py | 1 + apio/scons/scons_handler.py | 500 ++++++++++++++++++++++-------------- 5 files changed, 318 insertions(+), 186 deletions(-) diff --git a/apio/scons/plugin_base.py b/apio/scons/plugin_base.py index 72d3e99d..bd8db851 100644 --- a/apio/scons/plugin_base.py +++ b/apio/scons/plugin_base.py @@ -33,6 +33,7 @@ class ArchPluginInfo: """Provides information about the plugin.""" constrains_file_ext: str + bin_file_suffix: str clk_name_index: int diff --git a/apio/scons/plugin_ecp5.py b/apio/scons/plugin_ecp5.py index 2be68dc4..e609cd80 100644 --- a/apio/scons/plugin_ecp5.py +++ b/apio/scons/plugin_ecp5.py @@ -49,6 +49,7 @@ def plugin_info(self) -> ArchPluginInfo: """Return plugin specific parameters.""" return ArchPluginInfo( constrains_file_ext=".lpf", + bin_file_suffix=".bit", clk_name_index=2, ) diff --git a/apio/scons/plugin_gowin.py b/apio/scons/plugin_gowin.py index 8fca8ad8..8e371891 100644 --- a/apio/scons/plugin_gowin.py +++ b/apio/scons/plugin_gowin.py @@ -46,6 +46,7 @@ def plugin_info(self) -> ArchPluginInfo: """Return plugin specific parameters.""" return ArchPluginInfo( constrains_file_ext=".cst", + bin_file_suffix=".fs", clk_name_index=0, ) diff --git a/apio/scons/plugin_ice40.py b/apio/scons/plugin_ice40.py index 3fd8b073..fef945ae 100644 --- a/apio/scons/plugin_ice40.py +++ b/apio/scons/plugin_ice40.py @@ -46,6 +46,7 @@ def plugin_info(self) -> ArchPluginInfo: """Return plugin specific parameters.""" return ArchPluginInfo( constrains_file_ext=".pcf", + bin_file_suffix=".bin", clk_name_index=0, ) diff --git a/apio/scons/scons_handler.py b/apio/scons/scons_handler.py index 558783a4..9a3c5d40 100644 --- a/apio/scons/scons_handler.py +++ b/apio/scons/scons_handler.py @@ -11,6 +11,7 @@ """Apio scons related utilities..""" import sys +from click import secho from SCons.Script import ARGUMENTS, COMMAND_LINE_TARGETS from google.protobuf import text_format from apio.scons.plugin_ice40 import PluginIce40 @@ -87,222 +88,349 @@ def start() -> None: # -- Invoke the handler. This services the scons request. scons_handler.execute() - def register_builders(self): - """Register the builders created by the arch plugin.""" + def _register_common_targets(self, synth_srcs): + """Register the common synth, pnr, and bitstream operations which + are used by a few top level targets. + """ + apio_env = self.apio_env + params = apio_env.params plugin = self.arch_plugin - # -- Builders for project building. - if apio_env.targeting("build", "upload", "report"): - apio_env.builder(SYNTH_BUILDER, plugin.synth_builder()) - apio_env.builder(PNR_BUILDER, plugin.pnr_builder()) - apio_env.builder(BITSTREAM_BUILDER, plugin.bitstream_builder()) + # -- Sanity check + assert apio_env.targeting("build", "upload", "report") - # -- Builders for simulations. - if apio_env.targeting("sim", "test"): - apio_env.builder( - TESTBENCH_COMPILE_BUILDER, plugin.testbench_compile_builder() - ) - apio_env.builder( - TESTBENCH_RUN_BUILDER, plugin.testbench_run_builder() - ) + # -- Synth builder and target. + apio_env.builder(SYNTH_BUILDER, plugin.synth_builder()) - # -- Builders for graph command. - if apio_env.targeting("graph"): - apio_env.builder(YOSYS_DOT_BUILDER, plugin.yosys_dot_builder()) - apio_env.builder( - GRAPHVIZ_RENDERER_BUILDER, plugin.graphviz_renderer_builder() - ) + synth_target = apio_env.builder_target( + builder_id=SYNTH_BUILDER, + target=TARGET, + sources=[synth_srcs], + always_build=(params.verbosity.all or params.verbosity.synth), + ) + + # -- Place-and -oute builder and target + apio_env.builder(PNR_BUILDER, plugin.pnr_builder()) + + pnr_target = apio_env.builder_target( + builder_id=PNR_BUILDER, + target=TARGET, + sources=[synth_target, self.arch_plugin.constrain_file()], + always_build=(params.verbosity.all or params.verbosity.pnr), + ) - # -- Builders for linting. - if apio_env.targeting("lint"): - apio_env.builder(LINT_CONFIG_BUILDER, plugin.lint_config_builder()) - apio_env.builder(LINT_BUILDER, plugin.lint_builder()) + # -- Bitstream builder builder and target + apio_env.builder(BITSTREAM_BUILDER, plugin.bitstream_builder()) - # pylint: disable=too-many-locals - def register_targets(self): - """Register the targers. This is architecture independent.""" + apio_env.builder_target( + builder_id=BITSTREAM_BUILDER, + target=TARGET, + sources=pnr_target, + ) + + def _register_build_target(self, synth_srcs): + """Register the 'build' target which creates the binary bitstream.""" + apio_env = self.apio_env + params = apio_env.params + plugin = self.arch_plugin + + # -- Sanity check + assert apio_env.targeting("build") + + # -- Register the common targets for synth, pnr, and bitstream. + self._register_common_targets(synth_srcs) + + # -- Top level "build" target. + apio_env.alias( + "build", + source=TARGET + plugin.plugin_info().bin_file_suffix, + allways_build=( + params.verbosity.all + or params.verbosity.synth + or params.verbosity.pnr + ), + ) + + def _register_upload_target(self, synth_srcs): + """Register the 'upload' target which upload the binary file + generated by the bitstream generator.""" apio_env = self.apio_env - params: SconsParams = apio_env.params plugin_info = self.arch_plugin.plugin_info() - # -- Get a list of the synthesizable files (e.g. "main.v") and a list - # -- of the testbench files (e.g. "main_tb.v") - synth_srcs, test_srcs = source_files(apio_env) + # -- Sanity check + assert apio_env.targeting("upload") - # -- Targets for project building. - if apio_env.targeting("build", "upload", "report"): - synth_target = apio_env.builder_target( - builder_id=SYNTH_BUILDER, - target=TARGET, - sources=[synth_srcs], - always_build=(params.verbosity.all or params.verbosity.synth), - ) - pnr_target = apio_env.builder_target( - builder_id=PNR_BUILDER, - target=TARGET, - sources=[synth_target, self.arch_plugin.constrain_file()], - always_build=(params.verbosity.all or params.verbosity.pnr), - ) - bin_target = apio_env.builder_target( - builder_id=BITSTREAM_BUILDER, - target=TARGET, - sources=pnr_target, - ) - apio_env.alias( - "build", - source=bin_target, - allways_build=( - params.verbosity.all - or params.verbosity.synth - or params.verbosity.pnr - ), - ) + # -- Register the common targets for synth, pnr, and bitstream. + self._register_common_targets(synth_srcs) - # -- Target for the "report" command. - if apio_env.targeting("report"): - apio_env.alias( - "report", - source=TARGET + ".pnr", - action=report_action( - plugin_info.clk_name_index, params.verbosity.pnr - ), - allways_build=True, - ) + # -- Create the top level 'upload' target. + apio_env.alias( + "upload", + source=TARGET + plugin_info.bin_file_suffix, + action=get_programmer_cmd(apio_env), + allways_build=True, + ) - # -- Target for the "upload" command. - if apio_env.targeting("upload"): - apio_env.alias( - "upload", - source=bin_target, - action=get_programmer_cmd(apio_env), - allways_build=True, - ) + def _register_report_target(self, synth_srcs): + """Registers the 'report' target which a report file from the + PNR generated .pnr file.""" + apio_env = self.apio_env + params = apio_env.params + plugin_info = self.arch_plugin.plugin_info() - # -- Targets for the "graph" command. - if apio_env.targeting("graph"): - dot_target = apio_env.builder_target( - builder_id=YOSYS_DOT_BUILDER, - target=TARGET, - sources=synth_srcs, - always_build=True, - ) - graphviz_target = apio_env.builder_target( - builder_id=GRAPHVIZ_RENDERER_BUILDER, - target=TARGET, - sources=dot_target, - always_build=True, - ) - apio_env.alias( - "graph", - source=graphviz_target, - allways_build=True, - ) + # -- Sanity check + assert apio_env.targeting("report") + + # -- Register the common targets for synth, pnr, and bitstream. + self._register_common_targets(synth_srcs) + + # -- Register the top level 'report' target. + apio_env.alias( + "report", + source=TARGET + ".pnr", + action=report_action( + plugin_info.clk_name_index, params.verbosity.pnr + ), + allways_build=True, + ) + + def _register_graph_target( + self, + synth_srcs, + ): + """Registers the 'graph' target which generates a .dot file using + yosys and renders it using graphviz.""" + apio_env = self.apio_env + params = apio_env.params + plugin = self.arch_plugin + + # -- Sanity check + assert apio_env.targeting("graph") + assert params.target.HasField("graph") + + # -- Create the .dot generation builder and target. + apio_env.builder(YOSYS_DOT_BUILDER, plugin.yosys_dot_builder()) + + dot_target = apio_env.builder_target( + builder_id=YOSYS_DOT_BUILDER, + target=TARGET, + sources=synth_srcs, + always_build=True, + ) + + # -- Create the rendering builder and target. + apio_env.builder( + GRAPHVIZ_RENDERER_BUILDER, plugin.graphviz_renderer_builder() + ) + graphviz_target = apio_env.builder_target( + builder_id=GRAPHVIZ_RENDERER_BUILDER, + target=TARGET, + sources=dot_target, + always_build=True, + ) + + # -- Create the top level "graph" target. + apio_env.alias( + "graph", + source=graphviz_target, + allways_build=True, + ) + + def _register_lint_target(self, synth_srcs, test_srcs): + """Registers the 'lint' target which creates a lint configuration file + and runs the linter.""" + + apio_env = self.apio_env + params = apio_env.params + plugin = self.arch_plugin + + # -- Sanity check + assert apio_env.targeting("lint") + assert params.target.HasField("lint") + + # -- Create the builder and target of the config file creation. + apio_env.builder(LINT_CONFIG_BUILDER, plugin.lint_config_builder()) + + lint_config_target = apio_env.builder_target( + builder_id=LINT_CONFIG_BUILDER, + target=TARGET, + sources=[], + ) + + # -- Create the builder and target the lint operation. + apio_env.builder(LINT_BUILDER, plugin.lint_builder()) + + lint_out_target = apio_env.builder_target( + builder_id=LINT_BUILDER, + target=TARGET, + sources=synth_srcs + test_srcs, + extra_dependecies=[lint_config_target], + ) + + # -- Create the top level "lint" target. + apio_env.alias( + "lint", + source=lint_out_target, + allways_build=True, + ) + + def _register_sim_target(self, synth_srcs, test_srcs): + """Registers the 'sim' targets which compiles and runs the + simulation of a testbench.""" + + apio_env = self.apio_env + params = apio_env.params + plugin = self.arch_plugin + + # -- Sanity check + assert apio_env.targeting("sim") + assert params.target.HasField("sim") + + # -- Get values. + sim_params = params.target.sim + testbench = sim_params.testbench # Optional. + + # -- Collect information for sim. + sim_config = get_sim_config(testbench, synth_srcs, test_srcs) + + # -- Compilation builder and target - # -- The 'sim' target and its dependencies, to simulate and display the - # -- results of a single testbench. - if apio_env.targeting("sim"): - # -- Sanity check - assert params.target.HasField("sim") + apio_env.builder( + TESTBENCH_COMPILE_BUILDER, plugin.testbench_compile_builder() + ) - # -- Get values. - sim_params = params.target.sim - testbench = sim_params.testbench # Optional. + sim_out_target = apio_env.builder_target( + builder_id=TESTBENCH_COMPILE_BUILDER, + target=sim_config.build_testbench_name, + sources=sim_config.srcs, + always_build=sim_params.force_sim, + ) - # -- Collect information for sim. - sim_config = get_sim_config(testbench, synth_srcs, test_srcs) + # -- Simulation builder and target.. + + apio_env.builder(TESTBENCH_RUN_BUILDER, plugin.testbench_run_builder()) + + sim_vcd_target = apio_env.builder_target( + builder_id=TESTBENCH_RUN_BUILDER, + target=sim_config.build_testbench_name, + sources=[sim_out_target], + always_build=sim_params, + ) + + # -- The top level "sim" target. + waves_target( + apio_env, + "sim", + sim_vcd_target, + sim_config, + allways_build=True, + ) + + def _register_test_target(self, synth_srcs, test_srcs): + """Registers 'test' target and its dependencies. Each testbench + is tested independently with its own set of sub-targets.""" + + apio_env = self.apio_env + params = apio_env.params + plugin = self.arch_plugin + + # -- Sanity check + assert apio_env.targeting("test") + assert params.target.HasField("test") + + # -- Collect the test related values. + test_params = params.target.test + tests_configs = get_tests_configs( + test_params.testbench, synth_srcs, test_srcs + ) + + # -- Create compilation and simulation targets. + apio_env.builder( + TESTBENCH_COMPILE_BUILDER, plugin.testbench_compile_builder() + ) + apio_env.builder(TESTBENCH_RUN_BUILDER, plugin.testbench_run_builder()) + + # -- Create targes for each testbench we are testing. + tests_targets = [] + for test_config in tests_configs: # -- Create the compilation target. - sim_out_target = apio_env.builder_target( + test_out_target = apio_env.builder_target( builder_id=TESTBENCH_COMPILE_BUILDER, - target=sim_config.build_testbench_name, - sources=sim_config.srcs, - always_build=sim_params.force_sim, + target=test_config.build_testbench_name, + sources=test_config.srcs, + always_build=True, ) # -- Create the simulation target. - sim_vcd_target = apio_env.builder_target( + test_vcd_target = apio_env.builder_target( builder_id=TESTBENCH_RUN_BUILDER, - target=sim_config.build_testbench_name, - sources=[sim_out_target], - always_build=sim_params, + target=test_config.build_testbench_name, + sources=[test_out_target], + always_build=True, ) - # -- Create the graphic viewer target. - waves_target( - apio_env, - "sim", - sim_vcd_target, - sim_config, - allways_build=True, - ) + # -- Append to the list of targets we need to execute. + tests_targets.append(test_vcd_target) - # -- The "test" target and its dependencies, to test one or more - # -- testbenches. - if apio_env.targeting("test"): - # -- Sanity check - assert params.target.HasField("test") + # -- The top level 'test' target. + apio_env.alias("test", source=tests_targets, allways_build=True) - # -- Collect the test related values. - test_params = params.target.test - tests_configs = get_tests_configs( - test_params.testbench, synth_srcs, test_srcs - ) + def execute(self): + """The entry point of the scons handler. It registers the builders + and targets for the selected command and scons executes in upon + return.""" - # -- Create targes for each testbench we are testing. - tests_targets = [] - for test_config in tests_configs: - - # -- Create the compilation target. - test_out_target = apio_env.builder_target( - builder_id=TESTBENCH_COMPILE_BUILDER, - target=test_config.build_testbench_name, - sources=test_config.srcs, - always_build=True, - ) - - # -- Create the simulation target. - test_vcd_target = apio_env.builder_target( - builder_id=TESTBENCH_RUN_BUILDER, - target=test_config.build_testbench_name, - sources=[test_out_target], - always_build=True, - ) - - # -- Append to the list of targets we need to execute. - tests_targets.append(test_vcd_target) - - # -- The top 'test' target. - apio_env.alias("test", source=tests_targets, allways_build=True) - - # -- Targets for the "lint" command. - if apio_env.targeting("lint"): - # -- Create the target that creates the lint config file. - lint_config_target = apio_env.builder_target( - builder_id=LINT_CONFIG_BUILDER, - target=TARGET, - sources=[], - ) - # -- Create the target that actually lints. - lint_out_target = apio_env.builder_target( - builder_id=LINT_BUILDER, - target=TARGET, - sources=synth_srcs + test_srcs, - extra_dependecies=[lint_config_target], - ) - # -- Create the top target. - apio_env.alias( - "lint", - source=lint_out_target, - allways_build=True, - ) + apio_env = self.apio_env - # -- Handle the cleanu of the artifact files. - if apio_env.scons_env.GetOption("clean"): + # -- Collect a list of the synthesizable files (e.g. "main.v") and a + # -- list of the testbench files (e.g. "main_tb.v") + synth_srcs, test_srcs = source_files(apio_env) + + # -- A cleanup request is passed as scons -c option rather than as + # -- a target. + targets = apio_env.command_line_targets + if not targets: + assert apio_env.scons_env.GetOption("clean") configure_cleanup(apio_env) + return - def execute(self): - """The entry point of the scons handler.""" - self.register_builders() - self.register_targets() + # -- Get the target, we expect exactly one. + assert len(targets) == 1, targets + target = targets[0] + + # -- Dispatch by target. + # -- Not using python 'match' statement for compatibility with + # -- python 3.9. + if target == "build": + self._register_build_target(synth_srcs) + + elif target == "upload": + self._register_upload_target(synth_srcs) + + elif target == "report": + self._register_report_target(synth_srcs) + + elif target == "graph": + self._register_graph_target(synth_srcs) + + elif target == "sim": + self._register_sim_target(synth_srcs, test_srcs) + + elif target == "test": + self._register_test_target(synth_srcs, test_srcs) + + elif target == "lint": + self._register_lint_target(synth_srcs, test_srcs) + + else: + secho( + f"Error: scons handler got unexpected target [{target}]", + fg="red", + ) + sys.exit(1) + + # -- Note that we just registered builders and target. The actual + # -- execution is done by scons once this method returns. From daa8a3189eb5dc8d8c1ff26ce702709d69cf94c2 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 20:17:40 -0800 Subject: [PATCH 10/12] Added a unit test for the scons parameters construction. --- test/managers/test_scons.py | 160 ++++++++++++++++++++++++++++++++++++ test/scons/testing.py | 3 + 2 files changed, 163 insertions(+) create mode 100644 test/managers/test_scons.py diff --git a/test/managers/test_scons.py b/test/managers/test_scons.py new file mode 100644 index 00000000..e67be1ff --- /dev/null +++ b/test/managers/test_scons.py @@ -0,0 +1,160 @@ +""" +Tests of the scons manager scons.py +""" + +from test.conftest import ApioRunner +from google.protobuf import text_format +from apio.proto.apio_pb2 import ( + SconsParams, + Verbosity, + TargetParams, + LintParams, +) +from apio.apio_context import ApioContext, ApioContextScope +from apio.managers.scons import SCons + +# R0801: Similar lines in 2 files +# pylint: disable=R0801 + +TEST_APIO_INI_DICT = { + # -- Requied. + "board": "alhambra-ii", + # -- Optional. + "top-module": "my_module", + "format-verible-options": "\n --aaa bbb\n --ccc ddd", + "yosys-synth-extra-options": "-dsp -xyz", +} + +# -- The non determinisitc values marked with TBD are patched by the test +# -- at runtime. +EXPECTED1 = """ +timestamp: "TBD" +arch: ICE40 +fpga_info { + fpga_id: "ice40hx4k-tq144-8k" + part_num: "ICE40HX4K-TQ144" + size: "8k" + ice40 { + type: "hx8k" + pack: "tq144:4k" + } +} +envrionment { + platform_id: "darwin_arm64" + is_debug: false + yosys_path: "TBD" + trellis_path: "TBD" +} +project { + board_id: "alhambra-ii" + top_module: "my_module" + yosys_synth_extra_options: "-dsp -xyz" +} +""" + +EXPECTED2 = """ +timestamp: "TBD" +arch: ICE40 +fpga_info { + fpga_id: "ice40hx4k-tq144-8k" + part_num: "ICE40HX4K-TQ144" + size: "8k" + ice40 { + type: "hx8k" + pack: "tq144:4k" + } +} +verbosity { + all: true + synth: true + pnr: true +} +envrionment { + platform_id: "darwin_arm64" + is_debug: false + yosys_path: "TBD" + trellis_path: "TBD" +} +project { + board_id: "alhambra-ii" + top_module: "my_module" + yosys_synth_extra_options: "-dsp -xyz" +} +target { + lint { + top_module: "my_module" + verilator_all: true + verilator_no_style: true + verilator_no_warns: "aa" + verilator_no_warns: "bb" + verilator_warns: "cc" + verilator_warns: "dd" + } +} +""" + + +def test_default_params(apio_runner: ApioRunner): + """Tests the construct_scons_params() with default values.""" + + with apio_runner.in_sandbox() as sb: + + # -- Setup a Scons object. + sb.write_apio_ini(TEST_APIO_INI_DICT) + apio_ctx = ApioContext(scope=ApioContextScope.PROJECT_REQUIRED) + scons = SCons(apio_ctx) + + # -- Get the actual value. + scons_params = scons.construct_scons_params() + + # -- Construct the expected value. We fill in non deterministic values. + expected = text_format.Parse(EXPECTED1, SconsParams()) + expected.timestamp = scons_params.timestamp + expected.envrionment.yosys_path = str( + sb.packages_dir / "oss-cad-suite/share/yosys" + ) + expected.envrionment.trellis_path = str( + sb.packages_dir / "oss-cad-suite/share/trellis" + ) + + # -- Compare actual to expected values. + assert str(scons_params) == str(expected) + + +def test_explicit_params(apio_runner: ApioRunner): + """Tests the construct_scons_params() method with values override..""" + + with apio_runner.in_sandbox() as sb: + + # -- Setup a Scons object. + sb.write_apio_ini(TEST_APIO_INI_DICT) + apio_ctx = ApioContext(scope=ApioContextScope.PROJECT_REQUIRED) + scons = SCons(apio_ctx) + + # -- Get the actual value. + target_params = TargetParams( + lint=LintParams( + top_module="my_module", + verilator_all=True, + verilator_no_style=True, + verilator_no_warns=["aa", "bb"], + verilator_warns=["cc", "dd"], + ) + ) + verbosity = Verbosity(all=True, synth=True, pnr=True) + scons_params = scons.construct_scons_params( + verbosity=verbosity, target_params=target_params + ) + + # -- Construct the expected value. We fill in non deterministic values. + expected = text_format.Parse(EXPECTED2, SconsParams()) + expected.timestamp = scons_params.timestamp + expected.envrionment.yosys_path = str( + sb.packages_dir / "oss-cad-suite/share/yosys" + ) + expected.envrionment.trellis_path = str( + sb.packages_dir / "oss-cad-suite/share/trellis" + ) + + # -- Compare actual to expected values. + assert str(scons_params) == str(expected) diff --git a/test/scons/testing.py b/test/scons/testing.py index ba16d546..442b6b26 100644 --- a/test/scons/testing.py +++ b/test/scons/testing.py @@ -11,6 +11,9 @@ from apio.scons.apio_env import ApioEnv from apio.proto.apio_pb2 import SconsParams, TargetParams +# R0801: Similar lines in 2 files +# pylint: disable=R0801 + TEST_PARAMS = """ timestamp: "20123412052" arch: ICE40 From 034e82cd8a6d7ec5bef0b847ea3e19b0d0cb70e4 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 21:53:36 -0800 Subject: [PATCH 11/12] Added an ice40 example that uses the -dsp option. --- test-examples/ice40/fomu/dsp/apio.ini | 7 ++++ test-examples/ice40/fomu/dsp/blink.v | 43 +++++++++++++++++++++++++ test-examples/ice40/fomu/dsp/pinout.pcf | 29 +++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 test-examples/ice40/fomu/dsp/apio.ini create mode 100644 test-examples/ice40/fomu/dsp/blink.v create mode 100644 test-examples/ice40/fomu/dsp/pinout.pcf diff --git a/test-examples/ice40/fomu/dsp/apio.ini b/test-examples/ice40/fomu/dsp/apio.ini new file mode 100644 index 00000000..a3676178 --- /dev/null +++ b/test-examples/ice40/fomu/dsp/apio.ini @@ -0,0 +1,7 @@ +[env] +board = fomu +top-module = top + +# This option enables the use of ICESTORM_DSP cells as viewed +# using the 'apio report' command. +yosys-synth-extra-options = -dsp diff --git a/test-examples/ice40/fomu/dsp/blink.v b/test-examples/ice40/fomu/dsp/blink.v new file mode 100644 index 00000000..90114520 --- /dev/null +++ b/test-examples/ice40/fomu/dsp/blink.v @@ -0,0 +1,43 @@ +// This example demonstrates the effect the -dsp has on the +// synthesis phase. See apio.ini for the option definition +// and use 'apio report' to see how yosys shifts the implemeation +// from ICESTORM_DSP and ICESTORM_LC cells. + +module top ( + + input clki, + input user_1, // serial data in. + output user_2, // serial data out + + // USB Pins (which should be statically driven if not being used). + output usb_dp, + output usb_dn, + output usb_dp_pu +); + + wire din = user_1; + wire dout = user_2; + + // Prevent the host from trying to connect to this design. + assign usb_dp = 1'b0; + assign usb_dn = 1'b0; + assign usb_dp_pu = 1'b0; + + // The size of each of the serial registers. + // TODO: Why this fails with N = 40. + localparam N = 32; + + // The two words we multiply. + reg [N-1:0] reg1; + reg [N-1:0] reg2; + + always @(posedge clki) begin + reg1 <= {reg1[N-2:0], din}; + reg2 <= {reg1[N-2:0], reg1[N-1]}; + end + + // We multiple and output the parity of the results + // to make sure the multiplication is not optimized out. + assign dout = ^(reg1 * reg2); + +endmodule diff --git a/test-examples/ice40/fomu/dsp/pinout.pcf b/test-examples/ice40/fomu/dsp/pinout.pcf new file mode 100644 index 00000000..ef4b169c --- /dev/null +++ b/test-examples/ice40/fomu/dsp/pinout.pcf @@ -0,0 +1,29 @@ +set_io -nowarn rgb0 A5 +set_io -nowarn rgb1 B5 +set_io -nowarn rgb2 C5 +set_io -nowarn pmod_1 E4 +set_io -nowarn pmod_2 D5 +set_io -nowarn pmod_3 E5 +set_io -nowarn pmod_4 F5 +set_io -nowarn pmoda_1 E4 +set_io -nowarn pmoda_2 D5 +set_io -nowarn pmoda_3 E5 +set_io -nowarn pmoda_4 F5 +set_io -nowarn clki F4 +set_io -nowarn user_1 E4 +set_io -nowarn user_2 D5 +set_io -nowarn user_3 E5 +set_io -nowarn user_4 F5 +set_io -nowarn touch_1 E4 +set_io -nowarn touch_2 D5 +set_io -nowarn touch_3 E5 +set_io -nowarn touch_4 F5 +set_io -nowarn spi_mosi F1 +set_io -nowarn spi_miso E1 +set_io -nowarn spi_clk D1 +set_io -nowarn spi_io2 F2 +set_io -nowarn spi_io3 B1 +set_io -nowarn spi_cs C1 +set_io -nowarn usb_dn A2 +set_io -nowarn usb_dp A1 +set_io -nowarn usb_dp_pu A4 From 4d31e784dcf940279f0e894aa89910ce67fea345 Mon Sep 17 00:00:00 2001 From: Zapta Date: Wed, 22 Jan 2025 22:06:02 -0800 Subject: [PATCH 12/12] Simplified the -dsp example. --- test-examples/ice40/fomu/dsp/blink.v | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/test-examples/ice40/fomu/dsp/blink.v b/test-examples/ice40/fomu/dsp/blink.v index 90114520..01b374ff 100644 --- a/test-examples/ice40/fomu/dsp/blink.v +++ b/test-examples/ice40/fomu/dsp/blink.v @@ -7,7 +7,7 @@ module top ( input clki, input user_1, // serial data in. - output user_2, // serial data out + output reg user_2, // serial data out // USB Pins (which should be statically driven if not being used). output usb_dp, @@ -15,9 +15,6 @@ module top ( output usb_dp_pu ); - wire din = user_1; - wire dout = user_2; - // Prevent the host from trying to connect to this design. assign usb_dp = 1'b0; assign usb_dn = 1'b0; @@ -32,12 +29,11 @@ module top ( reg [N-1:0] reg2; always @(posedge clki) begin - reg1 <= {reg1[N-2:0], din}; - reg2 <= {reg1[N-2:0], reg1[N-1]}; + // Shift din into 2 x N words + reg1 <= {reg1[N-2:0], user_1}; + reg2 <= {reg1[N-2:0], reg1[N-1]}; + // Multiply the words, and output the parity of the result. + user_2 <= ~^(reg1 * reg2); end - // We multiple and output the parity of the results - // to make sure the multiplication is not optimized out. - assign dout = ^(reg1 * reg2); - endmodule