Skip to content

Commit

Permalink
_toolchain,build.plat,vendor.*: add required_tools list and checks.
Browse files Browse the repository at this point in the history
  • Loading branch information
emilazy authored and whitequark committed Aug 31, 2019
1 parent 4e91710 commit c4e8ac7
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 22 deletions.
37 changes: 35 additions & 2 deletions nmigen/_toolchain.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
import os
import shutil


__all__ = ["get_tool"]
__all__ = ["ToolNotFound", "get_tool", "has_tool", "require_tool"]


class ToolNotFound(Exception):
pass


def _tool_env_var(name):
return name.upper().replace("-", "_")


def get_tool(name):
return os.environ.get(name.upper().replace("-", "_"), overrides.get(name, name))
return os.environ.get(_tool_env_var(name), overrides.get(name, name))


def has_tool(name):
return shutil.which(get_tool(name)) is not None


def require_tool(name):
env_var = _tool_env_var(name)
path = get_tool(name)
if shutil.which(path) is None:
if path == name:
raise ToolNotFound("Could not find required tool {} in PATH. Place "
"it directly in PATH or specify path explicitly "
"via the {} environment variable".
format(name, env_var))
else:
if os.getenv(env_var):
via = "the {} environment variable".format(env_var)
else:
via = "your packager's toolchain overrides. This is either an " \
"nMigen bug or a packaging error"
raise ToolNotFound("Could not find required tool {} in {} as "
"specified via {}".format(name, path, via))
return path


# Packages for systems like Nix can inject full paths to certain tools by adding them in
Expand Down
17 changes: 3 additions & 14 deletions nmigen/back/verilog.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,8 @@ class YosysError(Exception):


def _yosys_version():
yosys_path = get_tool("yosys")
try:
version = subprocess.check_output([yosys_path, "-V"], encoding="utf-8")
except FileNotFoundError as e:
if os.getenv("YOSYS"):
raise YosysError("Could not find Yosys in {} as specified via the YOSYS environment "
"variable".format(os.getenv("YOSYS"))) from e
elif yosys_path == "yosys":
raise YosysError("Could not find Yosys in PATH. Place `yosys` in PATH or specify "
"path explicitly via the YOSYS environment variable") from e
else:
raise

yosys_path = require_tool("yosys")
version = subprocess.check_output([yosys_path, "-V"], encoding="utf-8")
m = re.match(r"^Yosys ([\d.]+)(?:\+(\d+))?", version)
tag, offset = m[1], m[2] or 0
return tuple(map(int, tag.split("."))), offset
Expand Down Expand Up @@ -57,7 +46,7 @@ def _convert_il_text(il_text, strip_src):
""".format(il_text, " ".join(attr_map),
prune="# " if version == (0, 9) and offset == 0 else "")

popen = subprocess.Popen([os.getenv("YOSYS", "yosys"), "-q", "-"],
popen = subprocess.Popen([require_tool("yosys"), "-q", "-"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
Expand Down
15 changes: 11 additions & 4 deletions nmigen/build/plat.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@


class Platform(ResourceManager, metaclass=ABCMeta):
resources = abstractproperty()
connectors = abstractproperty()
default_clk = None
default_rst = None
resources = abstractproperty()
connectors = abstractproperty()
default_clk = None
default_rst = None
required_tools = abstractproperty()

def __init__(self):
super().__init__(self.resources, self.connectors)
Expand Down Expand Up @@ -63,6 +64,9 @@ def build(self, elaboratable, name="top",
build_dir="build", do_build=True,
program_opts=None, do_program=False,
**kwargs):
for tool in self.required_tools:
require_tool(tool)

plan = self.prepare(elaboratable, name, **kwargs)
if not do_build:
return plan
Expand All @@ -73,6 +77,9 @@ def build(self, elaboratable, name="top",

self.toolchain_program(products, name, **(program_opts or {}))

def has_required_tools(self):
return all(has_tool(name) for name in self.required_tools)

@abstractmethod
def create_missing_domain(self, name):
# Simple instantiation of a clock domain driven directly by the board clock and reset.
Expand Down
4 changes: 2 additions & 2 deletions nmigen/test/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ..hdl.ast import *
from ..hdl.ir import *
from ..back import rtlil
from .._toolchain import get_tool
from .._toolchain import require_tool


__all__ = ["FHDLTestCase"]
Expand Down Expand Up @@ -95,7 +95,7 @@ def assertFormal(self, spec, mode="bmc", depth=1):
script=script,
rtlil=rtlil.convert(Fragment.get(spec, platform="formal"))
)
with subprocess.Popen([get_tool("sby"), "-f", "-d", spec_name], cwd=spec_dir,
with subprocess.Popen([require_tool("sby"), "-f", "-d", spec_name], cwd=spec_dir,
universal_newlines=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc:
stdout, stderr = proc.communicate(config)
Expand Down
8 changes: 8 additions & 0 deletions nmigen/vendor/lattice_ecp5.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,14 @@ def __init__(self, *, toolchain="Trellis"):
assert toolchain in ("Trellis", "Diamond")
self.toolchain = toolchain

@property
def required_tools(self):
if self.toolchain == "Trellis":
return ["yosys", "nextpnr-ecp5", "ecppack"]
if self.toolchain == "Diamond":
return ["pnmainc", "ddtcmd"]
assert False

@property
def file_templates(self):
if self.toolchain == "Trellis":
Expand Down
6 changes: 6 additions & 0 deletions nmigen/vendor/lattice_ice40.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class LatticeICE40Platform(TemplatedPlatform):
device = abstractproperty()
package = abstractproperty()

required_tools = [
"yosys",
"nextpnr-ice40",
"icepack",
]

_nextpnr_device_options = {
"iCE40LP384": "--lp384",
"iCE40LP1K": "--lp1k",
Expand Down
2 changes: 2 additions & 0 deletions nmigen/vendor/xilinx_7series.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
package = abstractproperty()
speed = abstractproperty()

required_tools = ["vivado"]

file_templates = {
**TemplatedPlatform.build_script_templates,
"{{name}}.v": r"""
Expand Down
8 changes: 8 additions & 0 deletions nmigen/vendor/xilinx_spartan_3_6.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ class XilinxSpartan3Or6Platform(TemplatedPlatform):
package = abstractproperty()
speed = abstractproperty()

required_tools = [
"xst",
"ngdbuild",
"map",
"par",
"bitgen",
]

@property
def family(self):
device = self.device.upper()
Expand Down

0 comments on commit c4e8ac7

Please sign in to comment.