_toolchain,build.plat,vendor.*: add required_tools list and checks.
This commit is contained in:
parent
4e91710933
commit
c4e8ac734f
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -57,7 +46,7 @@ write_verilog -norename
|
|||
""".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,
|
||||
|
|
|
@ -20,10 +20,11 @@ __all__ = ["Platform", "TemplatedPlatform"]
|
|||
|
||||
|
||||
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)
|
||||
|
@ -63,6 +64,9 @@ class Platform(ResourceManager, metaclass=ABCMeta):
|
|||
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
|
||||
|
@ -73,6 +77,9 @@ class Platform(ResourceManager, metaclass=ABCMeta):
|
|||
|
||||
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.
|
||||
|
|
|
@ -11,7 +11,7 @@ from contextlib import contextmanager
|
|||
from ..hdl.ast import *
|
||||
from ..hdl.ir import *
|
||||
from ..back import rtlil
|
||||
from .._toolchain import get_tool
|
||||
from .._toolchain import require_tool
|
||||
|
||||
|
||||
__all__ = ["FHDLTestCase"]
|
||||
|
@ -95,7 +95,7 @@ class FHDLTestCase(unittest.TestCase):
|
|||
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)
|
||||
|
|
8
nmigen/vendor/lattice_ecp5.py
vendored
8
nmigen/vendor/lattice_ecp5.py
vendored
|
@ -239,6 +239,14 @@ class LatticeECP5Platform(TemplatedPlatform):
|
|||
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":
|
||||
|
|
6
nmigen/vendor/lattice_ice40.py
vendored
6
nmigen/vendor/lattice_ice40.py
vendored
|
@ -39,6 +39,12 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
device = abstractproperty()
|
||||
package = abstractproperty()
|
||||
|
||||
required_tools = [
|
||||
"yosys",
|
||||
"nextpnr-ice40",
|
||||
"icepack",
|
||||
]
|
||||
|
||||
_nextpnr_device_options = {
|
||||
"iCE40LP384": "--lp384",
|
||||
"iCE40LP1K": "--lp1k",
|
||||
|
|
2
nmigen/vendor/xilinx_7series.py
vendored
2
nmigen/vendor/xilinx_7series.py
vendored
|
@ -50,6 +50,8 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
|
|||
package = abstractproperty()
|
||||
speed = abstractproperty()
|
||||
|
||||
required_tools = ["vivado"]
|
||||
|
||||
file_templates = {
|
||||
**TemplatedPlatform.build_script_templates,
|
||||
"{{name}}.v": r"""
|
||||
|
|
8
nmigen/vendor/xilinx_spartan_3_6.py
vendored
8
nmigen/vendor/xilinx_spartan_3_6.py
vendored
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue