Ejemplo n.º 1
0
    def configure_main(self):
        # Pass apicula tool options to yosys and nextpnr
        self.edam["tool_options"] = {
            "yosys": {
                "arch": "gowin",
                "output_format": "json",
                "yosys_synth_options": [f"-json {self.name}.json"]
                + self.tool_options.get("yosys_synth_options", []),
                "yosys_as_subtool": True,
                "yosys_template": self.tool_options.get("yosys_template"),
            },
            "nextpnr": {
                "device": self.tool_options.get("device"),
                "nextpnr_options": self.tool_options.get("nextpnr_options", []),
            },
        }

        yosys = Yosys(self.edam, self.work_root)
        yosys.configure()

        nextpnr = Nextpnr(yosys.edam, self.work_root)
        nextpnr.flow_config = {"arch": "gowin"}
        nextpnr.configure()

        # Write Makefile
        commands = EdaCommands()
        commands.commands = yosys.commands

        commands.commands += nextpnr.commands

        # Image generation
        depends = self.name + ".pack"
        targets = self.name + ".fs"
        command = [
            "gowin_pack",
            "-d",
            self.tool_options.get("device"),
            "-o",
            targets,
            depends,
        ]
        commands.add(command, [targets], [depends])

        commands.set_default_target(targets)
        commands.write(os.path.join(self.work_root, "Makefile"))
Ejemplo n.º 2
0
    def configure_main(self):
        # Pass trellis tool options to yosys and nextpnr
        self.edam["tool_options"] = {
            "yosys": {
                "arch":
                "nexus",
                "output_format":
                "json",
                "yosys_synth_options":
                self.tool_options.get("yosys_synth_options", []),
                "yosys_as_subtool":
                True,
                "yosys_template":
                self.tool_options.get("yosys_template"),
            },
            "nextpnr": {
                "device": self.tool_options.get("device"),
                "nextpnr_options":
                self.tool_options.get("nextpnr_options", []),
            },
        }

        yosys = Yosys(self.edam, self.work_root)
        yosys.configure()

        nextpnr = Nextpnr(yosys.edam, self.work_root)
        nextpnr.flow_config = {"arch": "nexus"}
        nextpnr.configure()

        # Write Makefile
        commands = EdaCommands()
        commands.commands = yosys.commands

        commands.commands += nextpnr.commands

        # Image generation
        depends = self.name + ".fasm"
        targets = self.name + ".bit"
        command = ["prjoxide", "pack", depends, targets]
        commands.add(command, [targets], [depends])

        commands.set_default_target(self.name + ".bit")
        commands.write(os.path.join(self.work_root, "Makefile"))
Ejemplo n.º 3
0
    def configure_main(self):
        # pass mistral tool option to yosys and nextpnr

        self.edam["tool_options"] = {
            "yosys": {
                "arch":
                "intel_alm",
                "output_format":
                "json",
                "yosys_synth_options":
                self.tool_options.get("yosys_synth_options", []),
                "yosys_as_subtool":
                True,
                "yosys_template":
                self.tool_options.get("yosys_template"),
            },
            "nextpnr": {
                "device": self.tool_options.get("device"),
                "nextpnr_options":
                self.tool_options.get("nextpnr_options", []),
            },
        }

        yosys = Yosys(self.edam, self.work_root)
        yosys.configure()

        nextpnr = Nextpnr(yosys.edam, self.work_root)
        nextpnr.flow_config = {"arch": "mistral"}
        nextpnr.configure()

        # Write Makefile
        commands = EdaCommands()

        commands.commands = yosys.commands

        commands.commands += nextpnr.commands

        # Image generation

        commands.set_default_target(self.name + ".rbf")

        commands.write(os.path.join(self.work_root, "Makefile"))
Ejemplo n.º 4
0
    def configure_vpr(self):
        (src_files, incdirs) = self._get_fileset_files(force_slash=True)

        has_vhdl = "vhdlSource" in [x.file_type for x in src_files]
        has_vhdl2008 = "vhdlSource-2008" in [x.file_type for x in src_files]

        if has_vhdl or has_vhdl2008:
            logger.error("VHDL files are not supported in Yosys")
        file_list = []
        timing_constraints = []
        pins_constraints = []
        placement_constraints = []

        for f in src_files:
            if f.file_type in ["verilogSource"]:
                file_list.append(f.name)
            if f.file_type in ["SDC"]:
                timing_constraints.append(f.name)
            if f.file_type in ["PCF"]:
                pins_constraints.append(f.name)
            if f.file_type in ["xdc"]:
                placement_constraints.append(f.name)

        part = self.tool_options.get("part")
        package = self.tool_options.get("package")
        vendor = self.tool_options.get("vendor")

        if not part:
            logger.error('Missing required "part" parameter')
        if not package:
            logger.error('Missing required "package" parameter')

        if vendor == "xilinx":
            if "xc7a" in part:
                bitstream_device = "artix7"
            if "xc7z" in part:
                bitstream_device = "zynq7"
            if "xc7k" in part:
                bitstream_device = "kintex7"

            partname = part + package

            # a35t are in fact a50t
            # leave partname with 35 so we access correct DB
            if part == "xc7a35t":
                part = "xc7a50t"
            device_suffix = "test"
        elif vendor == "quicklogic":
            partname = package
            device_suffix = "wlcsp"
            bitstream_device = part + "_" + device_suffix

        _vo = self.tool_options.get("vpr_options")
        vpr_options = ["--additional_vpr_options", f'"{_vo}"'] if _vo else []
        pcf_opts = ["-p"] + pins_constraints if pins_constraints else []
        sdc_opts = ["-s"] + timing_constraints if timing_constraints else []
        xdc_opts = ["-x"
                    ] + placement_constraints if placement_constraints else []

        commands = EdaCommands()
        # Synthesis
        targets = self.toplevel + ".eblif"
        command = ["symbiflow_synth", "-t", self.toplevel]
        command += ["-v"] + file_list
        command += ["-d", bitstream_device]
        command += ["-p" if vendor == "xilinx" else "-P", partname]
        command += xdc_opts
        commands.add(command, [targets], [])

        # P&R
        eblif_opt = ["-e", self.toplevel + ".eblif"]
        device_opt = ["-d", part + "_" + device_suffix]

        depends = self.toplevel + ".eblif"
        targets = self.toplevel + ".net"
        command = ["symbiflow_pack"
                   ] + eblif_opt + device_opt + sdc_opts + vpr_options
        commands.add(command, [targets], [depends])

        depends = self.toplevel + ".net"
        targets = self.toplevel + ".place"
        command = ["symbiflow_place"] + eblif_opt + device_opt
        command += ["-n", depends, "-P", partname]
        command += sdc_opts + pcf_opts + vpr_options
        commands.add(command, [targets], [depends])

        depends = self.toplevel + ".place"
        targets = self.toplevel + ".route"
        command = ["symbiflow_route"] + eblif_opt + device_opt
        command += sdc_opts + vpr_options
        commands.add(command, [targets], [depends])

        depends = self.toplevel + ".route"
        targets = self.toplevel + ".fasm"
        command = ["symbiflow_write_fasm"] + eblif_opt + device_opt
        command += sdc_opts + vpr_options
        commands.add(command, [targets], [depends])

        depends = self.toplevel + ".fasm"
        targets = self.toplevel + ".bit"
        command = ["symbiflow_write_bitstream"] + ["-d", bitstream_device]
        command += ["-f", depends]
        command += ["-p" if vendor == "xilinx" else "-P", partname]
        command += ["-b", targets]
        commands.add(command, [targets], [depends])

        commands.set_default_target(targets)
        commands.write(os.path.join(self.work_root, "Makefile"))
Ejemplo n.º 5
0
    def configure_nextpnr(self):
        (src_files, incdirs) = self._get_fileset_files(force_slash=True)
        vendor = self.tool_options.get("vendor")

        # Yosys configuration
        yosys_synth_options = self.tool_options.get("yosys_synth_options", "")
        yosys_template = self.tool_options.get("yosys_template")
        yosys_edam = {
            "files": self.files,
            "name": self.name,
            "toplevel": self.toplevel,
            "parameters": self.parameters,
            "tool_options": {
                "yosys": {
                    "arch": vendor,
                    "output_format": "json",
                    "yosys_synth_options": yosys_synth_options,
                    "yosys_template": yosys_template,
                    "yosys_as_subtool": True,
                }
            },
        }

        yosys = getattr(import_module("edalize.yosys"),
                        "Yosys")(yosys_edam, self.work_root)
        yosys.configure()

        # Nextpnr configuration
        arch = self.tool_options.get("arch")
        if arch not in self.archs:
            logger.error(
                'Missing or invalid "arch" parameter: {} in "tool_options"'.
                format(arch))

        package = self.tool_options.get("package")
        if not package:
            logger.error('Missing required "package" parameter')

        part = self.tool_options.get("part")
        if not part:
            logger.error('Missing required "part" parameter')

        target_family = None
        for family in getattr(self, "fpga_interchange_families"):
            if family in part:
                target_family = family
                break

        if target_family is None and arch == "fpga_interchange":
            logger.error(
                "Couldn't find family for part: {}. Available families: {}".
                format(part,
                       ", ".join(getattr(self, "fpga_interchange_families"))))

        chipdb = None
        device = None
        placement_constraints = []

        for f in src_files:
            if f.file_type in ["bba"]:
                chipdb = f.name
            elif f.file_type in ["device"]:
                device = f.name
            elif f.file_type in ["xdc"]:
                placement_constraints.append(f.name)
            else:
                continue

        if not chipdb:
            logger.error("Missing required chipdb file")

        if placement_constraints == []:
            logger.error("Missing required XDC file(s)")

        if device is None and arch == "fpga_interchange":
            logger.error(
                'Missing required ".device" file for "fpga_interchange" arch')

        nextpnr_options = self.tool_options.get("nextpnr_options", "")
        partname = part + package
        # Strip speedgrade string when using fpga_interchange
        package = package.split("-")[0] if arch == "fpga_interchange" else None

        if "xc7a" in part:
            bitstream_device = "artix7"
        if "xc7z" in part:
            bitstream_device = "zynq7"
        if "xc7k" in part:
            bitstream_device = "kintex7"

        depends = self.name + ".json"
        xdcs = []
        for x in placement_constraints:
            xdcs += ["--xdc", x]

        commands = EdaCommands()
        commands.commands = yosys.commands
        if arch == "fpga_interchange":
            commands.header += """ifndef INTERCHANGE_SCHEMA_PATH
$(error Environment variable INTERCHANGE_SCHEMA_PATH was not found. It should be set to <fpga-interchange-schema path>/interchange)
endif

"""
            targets = self.name + ".netlist"
            command = ["python", "-m", "fpga_interchange.yosys_json"]
            command += ["--schema_dir", "$(INTERCHANGE_SCHEMA_PATH)"]
            command += ["--device", device]
            command += ["--top", self.toplevel]
            command += [depends, targets]
            commands.add(command, [targets], [depends])

            depends = self.name + ".netlist"
            targets = self.name + ".phys"
            command = ["nextpnr-" + arch, "--chipdb", chipdb]
            command += ["--package", package]
            command += xdcs
            command += ["--netlist", depends]
            command += ["--write", self.name + ".routed.json"]
            command += ["--phys", targets]
            command += [nextpnr_options]
            commands.add(command, [targets], [depends])

            depends = self.name + ".phys"
            targets = self.name + ".fasm"
            command = ["python", "-m", "fpga_interchange.fasm_generator"]
            command += ["--schema_dir", "$(INTERCHANGE_SCHEMA_PATH)"]
            command += [
                "--family",
                family,
                device,
                self.name + ".netlist",
                depends,
                targets,
            ]
            commands.add(command, [targets], [depends])
        else:
            targets = self.name + ".fasm"
            command = ["nextpnr-" + arch, "--chipdb", chipdb]
            command += xdcs
            command += ["--json", depends]
            command += ["--write", self.name + ".routed.json"]
            command += ["--fasm", targets]
            command += ["--log", "nextpnr.log"]
            command += [nextpnr_options]
            commands.add(command, [targets], [depends])

        depends = self.name + ".fasm"
        targets = self.name + ".bit"
        command = ["symbiflow_write_bitstream", "-d", bitstream_device]
        command += ["-f", depends, "-p", partname, "-b", targets]
        commands.add(command, [targets], [depends])

        commands.set_default_target(targets)
        commands.write(os.path.join(self.work_root, "Makefile"))
Ejemplo n.º 6
0
    def configure_main(self):
        (src_files, incdirs) = self._get_fileset_files()
        synth_out = self.name + "_synth.v"

        device = self.tool_options.get("device")
        if not device:
            raise RuntimeError("Missing required option 'device' for p_r")

        match = re.search("^CCGM1A([1-9]{1,2})$", device)
        if not match:
            raise RuntimeError("{} is not known device name".format(device))

        device_number = match.groups()[0]
        if device_number not in ["1", "2", "4", "9", "16", "25"]:
            raise RuntimeError(
                "Rel. size {} is not unsupported".format(device_number))

        ccf_file = None

        for f in src_files:
            if f.file_type == "CCF":
                if ccf_file:
                    raise RuntimeError(
                        "p_r only supports one ccf file. Found {} and {}".
                        format(ccf_file, f.name))
                else:
                    ccf_file = f.name

        # Pass trellis tool options to yosys
        self.edam["tool_options"] = {
            "yosys": {
                "arch":
                "gatemate",
                "output_format":
                "verilog",
                "output_name":
                synth_out,
                "yosys_synth_options":
                self.tool_options.get("yosys_synth_options", []),
                "yosys_as_subtool":
                True,
                "yosys_template":
                self.tool_options.get("yosys_template"),
            },
        }

        yosys = Yosys(self.edam, self.work_root)
        yosys.configure()

        # Write Makefile
        commands = EdaCommands()
        commands.commands = yosys.commands

        # PnR & image generation
        targets = self.name + "_00.cfg.bit"
        command = [
            "p_r",
            "-A",
            device_number,
            "-i",
            synth_out,
            "-o",
            self.name,
            "-lib",
            "ccag",
            " ".join(self.tool_options.get("p_r_options", "")),
        ]
        if ccf_file is not None:
            command += ["-ccf", ccf_file]
        commands.add(command, [targets], [synth_out])

        commands.set_default_target(targets)
        commands.write(os.path.join(self.work_root, "Makefile"))
Ejemplo n.º 7
0
    def configure(self, edam):
        """
        Configuration is the first phase of the build.

        This writes the project TCL files and Makefile. It first collects all
        sources, IPs and constraints and then writes them to the TCL file along
        with the build steps.
        """
        super().configure(edam)
        src_files = []
        incdirs = []
        edif_files = []
        has_vhdl2008 = False
        has_xci = False
        unused_files = []
        bd_files = []

        for f in self.files:
            cmd = ""
            if f["file_type"].startswith("verilogSource"):
                cmd = "read_verilog"
            elif f["file_type"].startswith("systemVerilogSource"):
                cmd = "read_verilog -sv"
            elif f["file_type"] == "tclSource":
                cmd = "source"
            elif f["file_type"] == "edif":
                cmd = "read_edif"
                edif_files.append(f["name"])
            elif f["file_type"].startswith("vhdlSource"):
                cmd = "read_vhdl"
                if f["file_type"] == "vhdlSource-2008":
                    has_vhdl2008 = True
                    cmd += " -vhdl2008"
                if f.get("logical_name"):
                    cmd += " -library " + f["logical_name"]
            elif f["file_type"] == "xci":
                cmd = "read_ip"
                has_xci = True
            elif f["file_type"] == "xdc":
                cmd = "read_xdc"
            elif f["file_type"] == "SDC":
                cmd = "read_xdc -unmanaged"
            elif f["file_type"] == "mem":
                cmd = "read_mem"
            elif f["file_type"] == "bd":
                cmd = "read_bd"
                bd_files.append(f["name"])

            if cmd:
                if not self._add_include_dir(f, incdirs):
                    src_files.append(cmd + " {" + f["name"] + "}")
            else:
                unused_files.append(f)
        template_vars = {
            "name": self.name,
            "src_files": "\n".join(src_files),
            "incdirs": incdirs + ["."],
            "tool_options": self.tool_options,
            "toplevel": self.toplevel,
            "vlogparam": self.vlogparam,
            "vlogdefine": self.vlogdefine,
            "generic": self.generic,
            "netlist_flow": bool(edif_files),
            "has_vhdl2008": has_vhdl2008,
            "has_xci": has_xci,
            "bd_files": bd_files,
        }
        self.render_template("vivado-project.tcl.j2", self.name + ".tcl",
                             template_vars)

        jobs = self.tool_options.get("jobs", None)

        run_template_vars = {
            "jobs": " -jobs " + str(jobs) if jobs is not None else ""
        }

        self.render_template("vivado-run.tcl.j2", self.name + "_run.tcl",
                             run_template_vars)

        synth_template_vars = {
            "jobs": " -jobs " + str(jobs) if jobs is not None else ""
        }

        self.render_template("vivado-synth.tcl.j2", self.name + "_synth.tcl",
                             synth_template_vars)
        # Write Makefile
        commands = EdaCommands()

        vivado_command = ["vivado", "-notrace", "-mode", "batch", "-source"]

        # Create project file
        project_file = self.name + ".xpr"
        tcl_file = [self.name + ".tcl"]
        commands.add(vivado_command + tcl_file, [project_file],
                     tcl_file + edif_files)
        synth = self.tool_options.get("synth", "vivado")
        if synth == "vivado":
            depends = [f"{self.name}_synth.tcl", project_file]
            targets = [f"{self.name}.runs/synth_1/__synthesis_is_complete__"]
            commands.add(vivado_command + depends, targets, depends)
        else:
            targets = edif_files

        commands.add([], ["synth"], targets)

        # Bitstream generation
        run_tcl = self.name + "_run.tcl"
        depends = [run_tcl, project_file]
        bitstream = self.name + ".bit"
        commands.add(vivado_command + depends, [bitstream], depends)

        commands.add(["vivado", project_file], ["build-gui"], [project_file])

        depends = [self.name + "_pgm.tcl", bitstream]
        command = [
            "vivado",
            "-quiet",
            "-nolog",
            "-notrace",
            "-mode",
            "batch",
            "-source",
            f"{self.name}_pgm.tcl",
            "-tclargs",
        ]
        part = self.tool_options.get("part", "")
        command += [part] if part else []
        command += [bitstream]
        commands.add(command, ["pgm"], depends)

        commands.set_default_target(bitstream)
        commands.write(os.path.join(self.work_root, "Makefile"))
        self.commands = commands.commands
        self.render_template("vivado-program.tcl.j2", self.name + "_pgm.tcl")
Ejemplo n.º 8
0
    def configure_main(self):
        # write Yosys tcl script file

        yosys_template = self.tool_options.get("yosys_template")

        incdirs = []
        file_table = []
        unused_files = []

        for f in self.files:
            cmd = ""
            if f["file_type"].startswith("verilogSource"):
                cmd = "read_verilog"
            elif f["file_type"].startswith("systemVerilogSource"):
                cmd = "read_verilog -sv"
            elif f["file_type"] == "tclSource":
                cmd = "source"

            if cmd:
                if not self._add_include_dir(f, incdirs):
                    file_table.append(cmd + " {" + f["name"] + "}")
            else:
                unused_files.append(f)

        self.edam["files"] = unused_files

        output_format = self.tool_options.get("output_format", "blif")
        default_target = f"{self.name}.{output_format}"

        self.edam["files"].append({
            "name":
            default_target,
            "file_type":
            "jsonNetlist" if output_format == "json" else output_format,
        })

        verilog_defines = []
        for key, value in self.vlogdefine.items():
            verilog_defines.append("{{{key} {value}}}".format(key=key,
                                                              value=value))

        verilog_params = []
        for key, value in self.vlogparam.items():
            if type(value) is str:
                value = '{"' + value + '"}'
            _s = r"chparam -set {} {} {}"
            verilog_params.append(
                _s.format(key, self._param_value_str(value), self.toplevel))

        arch = self.tool_options.get("arch", None)

        if not arch:
            logger.error("ERROR: arch is not defined.")

        template = yosys_template or "edalize_yosys_template.tcl"
        template_vars = {
            "verilog_defines":
            "{" + " ".join(verilog_defines) + "}",
            "verilog_params":
            "\n".join(verilog_params),
            "file_table":
            "\n".join(file_table),
            "incdirs":
            " ".join(["-I" + d for d in incdirs]),
            "top":
            self.toplevel,
            "synth_command":
            "synth_" + arch,
            "synth_options":
            " ".join(self.tool_options.get("yosys_synth_options", "")),
            "write_command":
            "write_" + output_format,
            "output_format":
            output_format,
            "output_opts":
            "-pvector bra " if arch == "xilinx" else "",
            "yosys_template":
            template,
            "name":
            self.name,
        }

        self.render_template("edalize_yosys_procs.tcl.j2",
                             "edalize_yosys_procs.tcl", template_vars)

        if not yosys_template:
            self.render_template("yosys-script-tcl.j2",
                                 "edalize_yosys_template.tcl", template_vars)

        commands = EdaCommands()
        commands.add(
            ["yosys", "-l", "yosys.log", "-p", f"'tcl {template}'"],
            [default_target],
            [template],
        )
        if self.tool_options.get("yosys_as_subtool"):
            self.commands = commands.commands
        else:
            commands.set_default_target(f"{self.name}.{output_format}")
            commands.write(os.path.join(self.work_root, "Makefile"))
Ejemplo n.º 9
0
class Edaflow(object):
    @classmethod
    def get_flow_options(cls):
        flow_opts = cls.FLOW_OPTIONS.copy()
        for tool in cls.FLOW:
            (tool_name, next_nodes, flow_defined_tool_options) = tool

            # Get available tool options from each tool in the flow
            class_tool_options = getattr(
                import_module(f"edalize.tools.{tool_name}"),
                tool_name.capitalize()).get_tool_options()
            for opt_name in class_tool_options:
                # Filter out tool options that are already set by the flow
                if not opt_name in flow_defined_tool_options:
                    flow_opts[opt_name] = class_tool_options[opt_name]
                    flow_opts[opt_name]["tool"] = tool_name
        return flow_opts

    def extract_flow_options(self):
        # Extract flow options from the EDAM
        flow_options = {}
        available_flow_options = [
            k for k, v in self.get_flow_options().items() if not v.get("tool")
        ]
        edam_flow_opts = self.edam.get("flow_options", {})
        for opt_name in list(edam_flow_opts.keys()):
            if opt_name in available_flow_options:  # self.get_flow_options():
                flow_options[opt_name] = edam_flow_opts.pop(opt_name)
        return flow_options

    # Filter out tool options for each tool from self.flow_options
    def extract_tool_options(self):
        tool_options = {}
        edam_flow_opts = self.edam.get("flow_options", {})
        for (tool_name, next_nodes, flow_defined_tool_options) in self.FLOW:

            # Get the tool class
            ToolClass = getattr(import_module(f"edalize.tools.{tool_name}"),
                                tool_name.capitalize())
            # Inject the flow-defined tool options to the EDAM
            tool_options[tool_name] = merge_dict(
                flow_defined_tool_options, tool_options.get(tool_name, {}))

            # Assign the EDAM-defined tool options to the right tool
            for opt_name in list(edam_flow_opts.keys()):
                if opt_name in ToolClass.get_tool_options():
                    tool_options[tool_name] = merge_dict(
                        tool_options[tool_name],
                        {opt_name: edam_flow_opts.pop(opt_name)},
                    )

            self.edam["tool_options"] = tool_options

    def build_tool_graph(self):
        # Instantiate the tools
        nodes = {}
        for (tool_name, next_nodes, flow_defined_tool_options) in self.FLOW:

            # Instantiate the tool class
            tool_inst = getattr(import_module(f"edalize.tools.{tool_name}"),
                                tool_name.capitalize())()

            # FIXME: Don't like injecting stuff like this
            tool_inst.next_nodes = next_nodes
            tool_inst.work_root = self.work_root

            nodes[tool_name] = tool_inst

        for name, node in nodes.items():
            for next_node in node.next_nodes:
                # Add backwards references
                nodes[next_node].prev_nodes.add(node)
        return nodes

    def configure_tools(self, nodes):
        def merge_edam(a, b):
            # Yeah, I know. It's just a temporary hack
            return b

        unconfigured_nodes = list(nodes.values())
        while unconfigured_nodes:
            node = unconfigured_nodes.pop(0)
            input_edam = {}

            # Check all dependencies are fulfilled
            all_deps_configured = True
            for n in node.prev_nodes:
                if n.edam:
                    input_edam = merge_edam(input_edam, n.edam)
                else:
                    all_deps_configured = False

            if all_deps_configured:
                # No input_edam means this is an input to the flow that should
                # receive the external EDAM.
                if not input_edam:
                    input_edam = self.edam

                node.configure(input_edam)

                # This is an input node. Inject dependency on pre_build scripts
                if not node.prev_nodes:
                    # Inject pre-build scripts before the first command
                    # that the node executes. Note that this isn't
                    # technically correct since the first command in
                    # the list might not be the first command executed
                    node.commands[0].order_only_deps = ["pre_build"]
                self.commands.commands += node.commands

    def add_scripts(self, depends, hook_name):
        last_script = depends
        for script in self.hooks.get(hook_name, []):

            # _env = self.env.copy()
            # if 'env' in script:
            #    _env.update(script['env'])

            targets = script["name"]
            command = script["cmd"]
            # FIXME : Add env vars
            self.commands.add(command, [targets], [last_script])

            last_script = script["name"]
        self.commands.add([], [hook_name], [last_script])

    def __init__(self, edam, work_root, verbose=False):
        self.edam = edam
        self.hooks = edam.get("hooks", {})

        # Extract all options that affects the flow rather than
        # just a single tool
        self.flow_options = self.extract_flow_options()

        # Rearrange tool_options so that each tool gets their
        # own tool_options
        self.extract_tool_options()

        self.work_root = work_root

        self.stdout = None
        self.stderr = None
        self.commands = EdaCommands()

    def set_run_command(self):
        self.commands.add([], ["run"], ["pre_run"])

    def configure(self):

        # Add pre build hooks
        self.add_scripts("", "pre_build")

        # Instantiate all tools (nodes) and build a DAG of the flow
        nodes = self.build_tool_graph()

        # Configure the individual tools in the graph
        self.configure_tools(nodes)

        # Add post_build scripts to the end of the build chain
        self.add_scripts(self.commands.default_target, "post_build")
        self.commands.set_default_target("post_build")

        # Add commands to be executed during the run phase
        self.add_scripts("", "pre_run")
        self.set_run_command()
        self.add_scripts("run", "post_run")

        # Write out execution file
        self.commands.write(os.path.join(self.work_root, "Makefile"))

    def _run_tool(self, cmd, args=[], cwd=None, quiet=False):
        logger.debug("Running " + cmd)
        logger.debug("args  : " + " ".join(args))

        capture_output = quiet and not (self.verbose or self.stdout
                                        or self.stderr)
        try:
            cp = run(
                [cmd] + args,
                cwd=cwd,
                stdin=subprocess.PIPE,
                stdout=self.stdout,
                stderr=self.stderr,
                capture_output=capture_output,
                check=True,
            )
        except FileNotFoundError:
            _s = "Command '{}' not found. Make sure it is in $PATH".format(cmd)
            raise RuntimeError(_s)
        except subprocess.CalledProcessError as e:
            _s = "'{}' exited with an error: {}".format(e.cmd, e.returncode)
            logger.debug(_s)

            if e.stdout:
                logger.info(e.stdout.decode())
            if e.stderr:
                logger.error(e.stderr.decode())
                logger.debug("=== STDERR ===")
                logger.debug(e.stderr)

            raise RuntimeError(_s)
        return cp.returncode, cp.stdout, cp.stderr

    def build(self):
        # FIXME: Get run command (e.g. make, ninja, cloud thingie..) from self.commands
        self._run_tool("make", cwd=self.work_root)

    # Most flows won't have a run phase
    def run(self, args):
        pass
Ejemplo n.º 10
0
    def configure(self, edam):
        """
        Configuration is the first phase of the build.

        This writes the project TCL files and Makefile. It first collects all
        sources, IPs and constraints and then writes them to the TCL file along
        with the build steps.
        """
        super().configure(edam)

        src_files = []
        incdirs = set()

        file_netlist = []
        timing_constraints = []

        for f in src_files:
            if f.file_type in ["blif", "eblif"]:
                file_netlist.append(f.name)
            if f.file_type in ["SDC"]:
                timing_constraints.append(f.name)

        arch_xml = self.tool_options.get("arch_xml")
        if not arch_xml:
            logger.error('Missing required "arch" parameter')

        _vo = self.tool_options.get("vpr_options")

        vpr_options = _vo if _vo else []
        sdc_opts = ["--sdc_file"
                    ] + timing_constraints if timing_constraints else []

        commands = EdaCommands()

        depends = self.name + ".blif"
        targets = self.name + ".net"
        command = ["vpr", arch_xml, self.name + ".blif", "--pack"]
        command += sdc_opts + vpr_options
        commands.add(command, [targets], [depends])

        depends = self.name + ".net"
        targets = self.name + ".place"
        command = ["vpr", arch_xml, self.name + ".blif", "--place"]
        command += sdc_opts + vpr_options
        commands.add(command, [targets], [depends])

        depends = self.name + ".place"
        targets = self.name + ".route"
        command = ["vpr", arch_xml, self.name + ".blif", "--route"]
        command += sdc_opts + vpr_options
        commands.add(command, [targets], [depends])

        depends = self.name + ".route"
        targets = self.name + ".analysis"
        command = ["vpr", arch_xml, self.name + ".blif", "--analysis"]
        command += sdc_opts + vpr_options
        commands.add(command, [targets], [depends])

        for ext in [".net", ".place", ".route", ".analysis"]:
            self.edam["files"].append({
                "name": self.name + str(ext),
                "file_type": "vpr_" + str(ext[1:])
            })

        self.commands = commands.commands
        commands.set_default_target(targets)
        commands.write(os.path.join(self.work_root, "Makefile"))