From: Florent Kermarrec Date: Mon, 12 Nov 2018 09:23:10 +0000 (+0100) Subject: build/lattice: import changes from migen X-Git-Tag: 24jan2021_ls180~1506 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=cb86728ad14bae38e60f8d195c00afa4690a5913;p=litex.git build/lattice: import changes from migen --- diff --git a/litex/build/altera/quartus.py b/litex/build/altera/quartus.py index 0dcdb53a..76ec775c 100644 --- a/litex/build/altera/quartus.py +++ b/litex/build/altera/quartus.py @@ -92,7 +92,7 @@ def _build_files(device, sources, vincpaths, named_sc, named_pc, build_name): def _run_quartus(build_name, quartus_path): - build_script_contents = """# Autogenerated by LiteX + build_script_contents = """# Autogenerated by Migen set -e diff --git a/litex/build/lattice/common.py b/litex/build/lattice/common.py index 4e843295..a4f21130 100644 --- a/litex/build/lattice/common.py +++ b/litex/build/lattice/common.py @@ -35,14 +35,13 @@ class LatticeECPXDDROutput: def lower(dr): return LatticeECPXDDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk) - lattice_ecpx_special_overrides = { AsyncResetSynchronizer: LatticeECPXAsyncResetSynchronizer, - DDROutput: LatticeECPXDDROutput + DDROutput: LatticeECPXDDROutput } -class LatticeECPXPrjTrellisTristateImpl(Module): +class LatticeECPXTrellisTristateImpl(Module): def __init__(self, io, o, oe, i): nbits, sign = value_bits_sign(io) for bit in range(nbits): @@ -55,15 +54,14 @@ class LatticeECPXPrjTrellisTristateImpl(Module): i_T=~oe, ) -class LatticeECPXPrjTrellisTristate(Module): +class LatticeECPXTrellisTristate(Module): @staticmethod def lower(dr): - return LatticeECPXPrjTrellisTristateImpl(dr.target, dr.o, dr.oe, dr.i) + return LatticeECPXTrellisTristateImpl(dr.target, dr.o, dr.oe, dr.i) - -lattice_ecpx_prjtrellis_special_overrides = { +lattice_ecpx_trellis_special_overrides = { AsyncResetSynchronizer: LatticeECPXAsyncResetSynchronizer, - Tristate: LatticeECPXPrjTrellisTristate, + Tristate: LatticeECPXTrellisTristate, DDROutput: LatticeECPXDDROutput } @@ -125,7 +123,6 @@ class LatticeiCE40DifferentialOutput: def lower(dr): return LatticeiCE40DifferentialOutputImpl(dr.i, dr.o_p, dr.o_n) - lattice_ice40_special_overrides = { AsyncResetSynchronizer: LatticeiCE40AsyncResetSynchronizer, Tristate: LatticeiCE40Tristate, diff --git a/litex/build/lattice/diamond.py b/litex/build/lattice/diamond.py index 17516aa4..1569f567 100644 --- a/litex/build/lattice/diamond.py +++ b/litex/build/lattice/diamond.py @@ -15,13 +15,17 @@ from litex.build import tools from litex.build.lattice import common +def _produces_jedec(device): + return device.startswith("LCMX") + + def _format_constraint(c): if isinstance(c, Pins): return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"") elif isinstance(c, IOStandard): return ("IOBUF PORT ", " IO_TYPE=" + c.name) elif isinstance(c, Misc): - return c.misc + return ("IOBUF PORT ", " " + c.misc) def _format_lpf(signame, pin, others, resname): @@ -48,56 +52,87 @@ def _build_lpf(named_sc, named_pc): def _build_files(device, sources, vincpaths, build_name): tcl = [] - tcl.append("prj_project new -name \"{}\" -impl \"implementation\" -dev {} -synthesis \"synplify\"".format(build_name, device)) + tcl.append("prj_project new -name \"{}\" -impl \"impl\" -dev {} -synthesis \"synplify\"".format(build_name, device)) for path in vincpaths: tcl.append("prj_impl option {include path} {\"" + path + "\"}") for filename, language, library in sources: tcl.append("prj_src add \"" + filename + "\" -work " + library) tcl.append("prj_impl option top \"{}\"".format(build_name)) - tcl.append("prj_run Synthesis -impl implementation -forceOne") - tcl.append("prj_run Translate -impl implementation") - tcl.append("prj_run Map -impl implementation") - tcl.append("prj_run PAR -impl implementation") - tcl.append("prj_run Export -impl implementation -task Bitgen") + tcl.append("prj_project save") + tcl.append("prj_run Synthesis -impl impl -forceOne") + tcl.append("prj_run Translate -impl impl") + tcl.append("prj_run Map -impl impl") + tcl.append("prj_run PAR -impl impl") + tcl.append("prj_run Export -impl impl -task Bitgen") + if _produces_jedec(device): + tcl.append("prj_run Export -impl impl -task Jedecgen") tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) -def _run_diamond(build_name, toolchain_path, ver=None): - if sys.platform == "win32" or sys.platform == "cygwin": - build_script_contents = "REM Autogenerated by LiteX\n" - build_script_contents += "pnmainc " + build_name + ".tcl\n" - build_script_file = "build_" + build_name + ".bat" - tools.write_to_file(build_script_file, build_script_contents) - r = subprocess.call([build_script_file]) - shutil.copy(os.path.join("implementation", build_name + "_implementation.bit"), build_name + ".bit") - shutil.copy(os.path.join("implementation", build_name + "_implementation.jed"), build_name + ".jed") - elif sys.platform == "linux": - bindir = os.path.join(toolchain_path, 'bin/lin64') - envfile = os.path.join(bindir, 'diamond_env') - build_script_contents = "# Autogenerated by LiteX\n" - build_script_contents += "set -e\n" - build_script_contents += "bindir='{}/'\n".format(bindir) - build_script_contents += "source {}\n".format(envfile) - build_script_contents += "diamondc {}.tcl | tee build.log\n".format(build_name) - build_script_file = "build_{}.sh".format(build_name) - - tools.write_to_file(build_script_file, build_script_contents) - r = subprocess.call(["bash", build_script_file]) - shutil.copy(os.path.join("implementation", build_name + "_implementation.bit"), build_name + ".bit") +def _build_script(build_name, device, toolchain_path, ver=None): + if sys.platform in ("win32", "cygwin"): + script_ext = ".bat" + build_script_contents = "@echo off\nrem Autogenerated by Migen\n\n" + copy_stmt = "copy" + fail_stmt = " || exit /b" + else: + script_ext = ".sh" + build_script_contents = "# Autogenerated by Migen\nset -e\n\n" + copy_stmt = "cp" + fail_stmt = "" + + if sys.platform not in ("win32", "cygwin"): + build_script_contents += "bindir={}\n".format(toolchain_path) + build_script_contents += ". ${{bindir}}/diamond_env{fail_stmt}\n".format( + fail_stmt=fail_stmt) + build_script_contents += "{pnmainc} {tcl_script}{fail_stmt}\n".format( + pnmainc=os.path.join(toolchain_path, "pnmainc"), + tcl_script=build_name + ".tcl", + fail_stmt=fail_stmt) + for ext in (".bit", ".jed"): + if ext == ".jed" and not _produces_jedec(device): + continue + build_script_contents += "{copy_stmt} {diamond_product} {migen_product}" \ + "{fail_stmt}\n".format( + copy_stmt=copy_stmt, + fail_stmt=fail_stmt, + diamond_product=os.path.join("impl", build_name + "_impl" + ext), + migen_product=build_name + ext) + + build_script_file = "build_" + build_name + script_ext + tools.write_to_file(build_script_file, build_script_contents, + force_unix=False) + return build_script_file + + +def _run_script(script): + if sys.platform in ("win32", "cygwin"): + shell = ["cmd", "/c"] else: - raise NotImplementedError + shell = ["bash"] - if r != 0: + if subprocess.call(shell + [script]) != 0: raise OSError("Subprocess failed") class LatticeDiamondToolchain: - attr_translate = DummyAttrTranslate() + attr_translate = { + # FIXME: document + "keep": ("syn_keep", "true"), + "no_retiming": ("syn_no_retiming", "true"), + "async_reg": None, + "mr_ff": None, + "mr_false_path": None, + "ars_ff1": None, + "ars_ff2": None, + "ars_false_path": None, + "no_shreg_extract": None + } special_overrides = common.lattice_ecpx_special_overrides def build(self, platform, fragment, build_dir="build", build_name="top", - toolchain_path="/opt/Diamond", run=True, **kwargs): + toolchain_path="/opt/Diamond", run=True): os.makedirs(build_dir, exist_ok=True) cwd = os.getcwd() os.chdir(build_dir) @@ -106,17 +141,18 @@ class LatticeDiamondToolchain: fragment = fragment.get_fragment() platform.finalize(fragment) - v_output = platform.get_verilog(fragment, name=build_name, **kwargs) + v_output = platform.get_verilog(fragment) named_sc, named_pc = platform.resolve_signals(v_output.ns) v_file = build_name + ".v" v_output.write(v_file) - sources = platform.sources + [(v_file, "verilog", "work")] + sources = platform.sources | {(v_file, "verilog", "work")} _build_files(platform.device, sources, platform.verilog_include_paths, build_name) tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc)) + script = _build_script(build_name, platform.device, toolchain_path) if run: - _run_diamond(build_name, toolchain_path) + _run_script(script) os.chdir(cwd) diff --git a/litex/build/lattice/icestorm.py b/litex/build/lattice/icestorm.py index f5b3586b..72eb0023 100644 --- a/litex/build/lattice/icestorm.py +++ b/litex/build/lattice/icestorm.py @@ -37,11 +37,11 @@ def _build_script(source, build_template, build_name, pnr_pkg_opts, icetime_pkg_opts, freq_constraint): if sys.platform in ("win32", "cygwin"): script_ext = ".bat" - build_script_contents = "@echo off\nrem Autogenerated by LiteX\n" + build_script_contents = "@echo off\nrem Autogenerated by Migen\n" fail_stmt = " || exit /b" else: script_ext = ".sh" - build_script_contents = "# Autogenerated by LiteX\nset -e\n" + build_script_contents = "# Autogenerated by Migen\nset -e\n" fail_stmt = "" for s in build_template: diff --git a/litex/build/lattice/platform.py b/litex/build/lattice/platform.py index 385fba5b..0091f97b 100644 --- a/litex/build/lattice/platform.py +++ b/litex/build/lattice/platform.py @@ -1,5 +1,5 @@ from litex.build.generic_platform import GenericPlatform -from litex.build.lattice import common, diamond, icestorm, prjtrellis +from litex.build.lattice import common, diamond, icestorm, trellis class LatticePlatform(GenericPlatform): @@ -9,8 +9,8 @@ class LatticePlatform(GenericPlatform): GenericPlatform.__init__(self, *args, **kwargs) if toolchain == "diamond": self.toolchain = diamond.LatticeDiamondToolchain() - elif toolchain == "prjtrellis": - self.toolchain = prjtrellis.LatticePrjTrellisToolchain() + elif toolchain == "trellis": + self.toolchain = trellis.LatticeTrellisToolchain() elif toolchain == "icestorm": self.bitstream_ext = ".bin" self.toolchain = icestorm.LatticeIceStormToolchain() diff --git a/litex/build/lattice/prjtrellis.py b/litex/build/lattice/prjtrellis.py deleted file mode 100644 index 79df493a..00000000 --- a/litex/build/lattice/prjtrellis.py +++ /dev/null @@ -1,151 +0,0 @@ -# This file is Copyright (c) 2018 Florent Kermarrec -# License: BSD - -import os -import subprocess - -from migen.fhdl.structure import _Fragment - -from litex.build.generic_platform import * -from litex.build import tools -from litex.build.lattice import common - -# TODO: -# - add timing constraint support. -# - check/document attr_translate. - -nextpnr_ecp5_architectures = { - "lfe5u-25f": "25k", - "lfe5u-45f": "45k", - "lfe5u-85f": "85k", - "lfe5um-25f": "um-25k", - "lfe5um-45f": "um-45k", - "lfe5um-85f": "um-85k", - "lfe5um5g-25f": "um5g-25k", - "lfe5um5g-45f": "um5g-45k", - "lfe5um5g-85f": "um5g-85k", -} - - -def _format_constraint(c): - if isinstance(c, Pins): - return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"") - elif isinstance(c, IOStandard): - return ("IOBUF PORT ", " IO_TYPE=" + c.name) - elif isinstance(c, Misc): - return ("IOBUF PORT ", " " + c.misc) - - -def _format_lpf(signame, pin, others, resname): - fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)] - r = "" - for pre, suf in fmt_c: - r += pre + "\"" + signame + "\"" + suf + ";\n" - return r - - -def _build_lpf(named_sc, named_pc): - r = "BLOCK RESETPATHS;\n" - r += "BLOCK ASYNCPATHS;\n" - for sig, pins, others, resname in named_sc: - if len(pins) > 1: - for i, p in enumerate(pins): - r += _format_lpf(sig + "[" + str(i) + "]", p, others, resname) - else: - r += _format_lpf(sig, pins[0], others, resname) - if named_pc: - r += "\n" + "\n\n".join(named_pc) - return r - - -def yosys_import_sources(platform): - includes = "" - reads = [] - for path in platform.verilog_include_paths: - includes += " -I" + path - for filename, language, library in platform.sources: - reads.append("read_{}{} {}".format( - language, includes, filename)) - return "\n".join(reads) - - -class LatticePrjTrellisToolchain: - attr_translate = { - # FIXME: document - "keep": ("keep", "true"), - "no_retiming": None, - "async_reg": None, - "mr_ff": None, - "mr_false_path": None, - "ars_ff1": None, - "ars_ff2": None, - "ars_false_path": None, - "no_shreg_extract": None - } - - special_overrides = common.lattice_ecpx_prjtrellis_special_overrides - - def build(self, platform, fragment, build_dir="build", build_name="top", - toolchain_path=None, run=True): - if toolchain_path is None: - toolchain_path = "/usr/share/trellis/" - os.makedirs(build_dir, exist_ok=True) - cwd = os.getcwd() - os.chdir(build_dir) - - # generate verilog - if not isinstance(fragment, _Fragment): - fragment = fragment.get_fragment() - platform.finalize(fragment) - - top_output = platform.get_verilog(fragment, name=build_name) - named_sc, named_pc = platform.resolve_signals(top_output.ns) - top_file = build_name + ".v" - top_output.write(top_file) - platform.add_source(top_file) - - # generate constraints - tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc)) - - # generate yosys script - yosys_script_file = build_name + ".ys" - yosys_script_contents = [ - yosys_import_sources(platform), - "synth_ecp5 -nomux -json {build_name}.json -top {build_name}" - ] - yosys_script_contents = "\n".join(yosys_script_contents) - yosys_script_contents = yosys_script_contents.format(build_name=build_name) - tools.write_to_file(yosys_script_file, yosys_script_contents) - - # transform platform.device to nextpnr's architecture / basecfg - (family, size, package) = platform.device.split("-") - architecture = nextpnr_ecp5_architectures[(family + "-" + size).lower()] - basecfg = "empty_" + (family + "-" + size).lower() + ".config" - basecfg = os.path.join(toolchain_path, "misc", "basecfgs", basecfg) - - # generate build script - build_script_file = "build_" + build_name + ".sh" - build_script_contents = [ - "yosys -q -l {build_name}.rpt {build_name}.ys", - "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --basecfg {basecfg} --{architecture}", - "ecppack {build_name}.config {build_name}.bit" - - ] - build_script_contents = "\n".join(build_script_contents) - build_script_contents = build_script_contents.format( - build_name=build_name, - architecture=architecture, - basecfg=basecfg) - tools.write_to_file(build_script_file, build_script_contents) - - # run scripts - if run: - if subprocess.call(["bash", build_script_file]) != 0: - raise OSError("Subprocess failed") - - os.chdir(cwd) - - return top_output.ns - - def add_period_constraint(self, platform, clk, period): - print("TODO: add_period_constraint") diff --git a/litex/build/lattice/trellis.py b/litex/build/lattice/trellis.py new file mode 100644 index 00000000..8f272920 --- /dev/null +++ b/litex/build/lattice/trellis.py @@ -0,0 +1,198 @@ +# This file is Copyright (c) 2018 Florent Kermarrec +# This file is Copyright (c) 2018 William D. Jones +# License: BSD + +import os +import subprocess +import sys + +from migen.fhdl.structure import _Fragment + +from litex.build.generic_platform import * +from litex.build import tools +from litex.build.lattice import common + +# TODO: +# - check/document attr_translate. + +nextpnr_ecp5_architectures = { + "lfe5u-25f": "25k", + "lfe5u-45f": "45k", + "lfe5u-85f": "85k", + "lfe5um-25f": "um-25k", + "lfe5um-45f": "um-45k", + "lfe5um-85f": "um-85k", + "lfe5um5g-25f": "um5g-25k", + "lfe5um5g-45f": "um5g-45k", + "lfe5um5g-85f": "um5g-85k", +} + + +def _format_constraint(c): + if isinstance(c, Pins): + return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"") + elif isinstance(c, IOStandard): + return ("IOBUF PORT ", " IO_TYPE=" + c.name) + elif isinstance(c, Misc): + return ("IOBUF PORT ", " " + c.misc) + + +def _format_lpf(signame, pin, others, resname): + fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)] + r = "" + print(fmt_c) + for pre, suf in fmt_c: + r += pre + "\"" + signame + "\"" + suf + ";\n" + return r + + +def _build_lpf(named_sc, named_pc): + r = "BLOCK RESETPATHS;\n" + r += "BLOCK ASYNCPATHS;\n" + for sig, pins, others, resname in named_sc: + if len(pins) > 1: + for i, p in enumerate(pins): + r += _format_lpf(sig + "[" + str(i) + "]", p, others, resname) + else: + r += _format_lpf(sig, pins[0], others, resname) + if named_pc: + r += "\n" + "\n\n".join(named_pc) + return r + + +def _build_script(source, build_template, build_name, architecture, + basecfg, freq_constraint): + if sys.platform in ("win32", "cygwin"): + script_ext = ".bat" + build_script_contents = "@echo off\nrem Autogenerated by Migen\n\n" + fail_stmt = " || exit /b" + else: + script_ext = ".sh" + build_script_contents = "# Autogenerated by Migen\nset -e\n\n" + fail_stmt = "" + + for s in build_template: + s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early. + build_script_contents += s_fail.format(build_name=build_name, + architecture=architecture, + basecfg=basecfg, + freq_constraint=freq_constraint, + fail_stmt=fail_stmt) + + build_script_file = "build_" + build_name + script_ext + tools.write_to_file(build_script_file, build_script_contents, + force_unix=False) + return build_script_file + + +def _run_script(script): + if sys.platform in ("win32", "cygwin"): + shell = ["cmd", "/c"] + else: + shell = ["bash"] + + if subprocess.call(shell + [script]) != 0: + raise OSError("Subprocess failed") + + +def yosys_import_sources(platform): + includes = "" + reads = [] + for path in platform.verilog_include_paths: + includes += " -I" + path + for filename, language, library in platform.sources: + reads.append("read_{}{} {}".format( + language, includes, filename)) + return "\n".join(reads) + + +class LatticeTrellisToolchain: + attr_translate = { + # FIXME: document + "keep": ("keep", "true"), + "no_retiming": None, + "async_reg": None, + "mr_ff": None, + "mr_false_path": None, + "ars_ff1": None, + "ars_ff2": None, + "ars_false_path": None, + "no_shreg_extract": None + } + + special_overrides = common.lattice_ecpx_trellis_special_overrides + + def __init__(self): + self.yosys_template = [ + "{read_files}", + "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0", + "synth_ecp5 -nomux -json {build_name}.json -top {build_name}", + ] + + self.build_template = [ + "yosys -q -l {build_name}.rpt {build_name}.ys", + "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --basecfg {basecfg} --{architecture} --freq {freq_constraint}", + "ecppack {build_name}.config {build_name}.bit" + ] + + self.freq_constraints = dict() + + def build(self, platform, fragment, build_dir="build", build_name="top", + toolchain_path=None, run=True): + if toolchain_path is None: + toolchain_path = "/usr/share/trellis/" + os.makedirs(build_dir, exist_ok=True) + cwd = os.getcwd() + os.chdir(build_dir) + + # generate verilog + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + platform.finalize(fragment) + + top_output = platform.get_verilog(fragment, name=build_name) + named_sc, named_pc = platform.resolve_signals(top_output.ns) + top_file = build_name + ".v" + top_output.write(top_file) + platform.add_source(top_file) + + # generate constraints + tools.write_to_file(build_name + ".lpf", + _build_lpf(named_sc, named_pc)) + + # generate yosys script + yosys_script_file = build_name + ".ys" + yosys_script_contents = "\n".join(_.format(build_name=build_name, + read_files=yosys_import_sources(platform)) + for _ in self.yosys_template) + tools.write_to_file(yosys_script_file, yosys_script_contents) + + # transform platform.device to nextpnr's architecture / basecfg + (family, size, package) = platform.device.split("-") + architecture = nextpnr_ecp5_architectures[(family + "-" + size).lower()] + basecfg = "empty_" + (family + "-" + size).lower() + ".config" + basecfg = os.path.join(toolchain_path, "misc", "basecfgs", basecfg) + freq_constraint = str(max(self.freq_constraints.values(), + default=0.0)) + + script = _build_script(False, self.build_template, build_name, + architecture, basecfg, freq_constraint) + + # run scripts + if run: + _run_script(script) + + os.chdir(cwd) + + return top_output.ns + + # Until nextpnr-ecp5 can handle multiple clock domains, use the same + # approach as the icestorm and use the fastest clock for timing + # constraints. + def add_period_constraint(self, platform, clk, period): + new_freq = 1000.0/period + + if clk not in self.freq_constraints.keys(): + self.freq_constraints[clk] = new_freq + else: + raise ConstraintError("Period constraint already added to signal.") diff --git a/litex/build/xilinx/ise.py b/litex/build/xilinx/ise.py index ef561f4b..1b5a6644 100644 --- a/litex/build/xilinx/ise.py +++ b/litex/build/xilinx/ise.py @@ -88,13 +88,13 @@ def _run_ise(build_name, ise_path, source, mode, ngdbuild_opt, source_cmd = "call " script_ext = ".bat" shell = ["cmd", "/c"] - build_script_contents = "@echo off\nrem Autogenerated by LiteX\n" + build_script_contents = "@echo off\nrem Autogenerated by Migen\n" fail_stmt = " || exit /b" else: source_cmd = "source " script_ext = ".sh" shell = ["bash"] - build_script_contents = "# Autogenerated by LiteX\nset -e\n" + build_script_contents = "# Autogenerated by Migen\nset -e\n" fail_stmt = "" if source: settings = common.settings(ise_path, ver, "ISE_DS") diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index a1f94d9f..4859c149 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -56,13 +56,13 @@ def _build_xdc(named_sc, named_pc): def _run_vivado(build_name, vivado_path, source, ver=None): if sys.platform == "win32" or sys.platform == "cygwin": - build_script_contents = "REM Autogenerated by LiteX\n" + build_script_contents = "REM Autogenerated by Migen\n" build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" build_script_file = "build_" + build_name + ".bat" tools.write_to_file(build_script_file, build_script_contents) command = build_script_file else: - build_script_contents = "# Autogenerated by LiteX\nset -e\n" + build_script_contents = "# Autogenerated by Migen\nset -e\n" # For backwards compatibility with ISE paths, also # look for a version in a subdirectory named "Vivado"