def __write_hw__(
     self, path: str, use_symlinks: bool = True, allow_deletion: bool = False
 ):
     # Make sure path is good
     opath = self.__check_and_get_output_path__(path)
     if not opath:
         raise AsFileError(path, "Could not create output folder!")
     opath = append_to_path(opath, "/")
     # Clean up if not empty
     try:
         as_build.prepare_output_path(None, opath, allow_deletion)
     except IOError as err:
         LOG.error(
             ("Could not prepare the output directory '%s'" "! - '%s'"),
             opath,
             str(err),
         )
         raise AsFileError(opath, "Could not write to output folder!")
     # Generate and collect hardware files
     try:
         self.__gen_hw__(opath, use_symlinks)
     except IOError:
         LOG.error(
             ("Cannot write to '%s'! - " "Could not create VHDL source files!"),
             opath,
         )
         return False
     except AsError as err:
         LOG.error(str(err))
         return False
예제 #2
0
def write_config_hc(chain, output_path: str):
    """! @brief Write the files 'as_config.[hc]'
    The files contain the build date, version string and configuration macros."""
    LOG.info("Generating as_config.c source file...")
    # Fetch and format todays date
    date_string = datetime.today().strftime("%Y-%m-%d")
    version_string = chain.parent.version
    hashstr = "'" + "', '".join(chain.get_hash()) + "'"
    filepath = append_to_path(output_path,
                              AS_CONFIG_C_NAME,
                              add_trailing_slash=False)

    try:
        with open(filepath, "w") as file:
            file.write(
                AS_CONFIG_C_TEMPLATE.format(
                    header=ASTERICS_HEADER_SW.format(
                        filename=AS_CONFIG_C_NAME,
                        description=AS_CONFIG_C_DESCRIPTION,
                    ),
                    hashstr=hashstr,
                    date=date_string,
                    version=version_string,
                ))
    except IOError as err:
        LOG.error("Could not write '%s'! '%s'", AS_CONFIG_C_NAME, str(err))
        raise AsFileError(
            filepath,
            detail=str(err),
            msg="Could not write to file",
        )

    LOG.info("Generating as_config.h source file...")
    filepath = append_to_path(output_path,
                              AS_CONFIG_H_NAME,
                              add_trailing_slash=False)
    try:
        with open(filepath, "w") as file:
            file.write(
                AS_CONFIG_H_TEMPLATE.format(header=ASTERICS_HEADER_SW.format(
                    filename=AS_CONFIG_H_NAME,
                    description=AS_CONFIG_H_DESCRIPTION,
                )))
    except IOError as err:
        LOG.error("Could not write '%s'! '%s'", AS_CONFIG_H_NAME, str(err))
        raise AsFileError(
            filepath,
            detail=str(err),
            msg="Could not write to file",
        )
예제 #3
0
    def write_module_group_vhd(self, folder: str, module_group: AsModuleGroup):
        """! @brief Generate the VHDL file for a module group (AsModuleGroup)"""
        LOG.info("Writing ASTERICS module group file '%s'.", module_group.name)
        filename = "{}.vhd".format(module_group.name)
        outfile = as_help.append_to_path(folder,
                                         filename,
                                         add_trailing_slash=False)

        # Generate the list of glue signals
        self.generate_glue_signal_strings(module_group.signals)

        header = self._generate_header_and_library_string(module_group)
        vhdl_list = []
        # Generate the module's entity declaration
        vhdl_list.extend(self._generate_entity(module_group, None))
        # Generate the toplevel architecture
        vhdl_list.extend(
            self._generate_module_group_architecture(module_group, None))
        # Open the output file
        with open(outfile, "w") as ofile:
            # Make sure we can write to the file
            if not ofile.writable():
                raise AsFileError(msg="File not writable", filename=filename)
            ofile.write(header)
            vhdl_write.write_list_to_file(vhdl_list, ofile)

        # Reset to init state
        self.clear_lists()
예제 #4
0
def prepare_output_path(source_path: str,
                        output_path: str,
                        allow_deletion: bool = False):
    """! @brief Copy the template directory tree for a blank system to output_path.
    @param source_path: path to the folder to copy.
    @param output_path: Where to copy source_path to.
    @param allow_deletion: If output_path is not empty delete the contents if
                      allow_deletion is True, else throw an error."""
    LOG.info("Preparing output project directory...")
    if os.path.isdir(output_path) and os.listdir(output_path):
        if allow_deletion:
            LOG.info("Directory already exists! Cleaning...")
            try:
                rmtree(output_path)
            except Exception as err:
                LOG.error(
                    "Could not clean project output directory '%s'! '%s'",
                    output_path,
                    str(err),
                )
                raise AsFileError(
                    output_path,
                    detail=str(err),
                    msg="'shutil.rmtree' failed to remove output directory!",
                )
            os.makedirs(output_path)
        else:
            raise AsFileError(
                output_path,
                "Output directory not empty!",
                "Output generation not started!",
            )
    if source_path is not None:
        LOG.info("Copying template project directory to output path...")
        try:
            copytree(source_path, output_path)
        except Exception as err:
            LOG.error(
                "Could not create system output tree in '%s'! '%s'",
                output_path,
                str(err),
            )
            raise AsFileError(
                output_path,
                detail=str(err),
                msg="Could not copy project template to output path!",
            )
    def write_asterics_vhd(self, folder: str):
        """Generate the Toplevel ASTERICS VHDL file"""
        LOG.info("Writing ASTERICS toplevel VHDL file...")
        # Generate path to the output file
        outfile = as_help.append_to_path(folder, as_static.AS_TOP_FILENAME, False)

        # Generate the list of glue signals
        self.generate_glue_signal_strings(self.chain.top.signals)

        library_use_str = ""
        lib_use_template = "use asterics.{};\n"
        module_entities = []
        for mod in self.chain.top.modules:
            if mod.entity_name not in module_entities:
                module_entities.append(mod.entity_name)
        for entity in module_entities:
            library_use_str += lib_use_template.format(entity)

        # Open the output file
        with open(outfile, "w") as ofile:
            # Make sure we can write to the file
            if not ofile.writable():
                raise AsFileError(
                    msg="File not writable", filename=as_static.AS_TOP_FILENAME
                )
            # Write the header
            ofile.write(
                as_static.HEADER.format(
                    entity_name=as_static.AS_TOP_ENTITY,
                    filename=as_static.AS_TOP_FILENAME,
                    longdesc=as_static.AS_TOP_DESC,
                    briefdesc=as_static.AS_TOP_DESC,
                )
            )
            # and the basic library 'use' declaration
            ofile.write(
                "\n{}{}{}\n".format(
                    as_static.LIBS_IEEE, as_static.LIBS_ASTERICS, library_use_str
                )
            )
            # Generate and write the entity descritpion
            self.__write_entity__(self.chain.top, ofile)
            # Generate and write the toplevel architecture
            self.__write_module_group_architecture__(self.chain.top, ofile)
            # Done writing to file
        # Reset to init state
        self.clear_lists()
예제 #6
0
 def __analyze__(self):
     LOG.info("Start analysis of VHDL file '%s' ...", self.vhdl_file)
     try:
         self.file_analyzed = False
         # Open the source file
         with open(self.vhdl_file) as file:
             LOG.debug("File opened, reading...")
             self.__analyze_readfile__(file)
     except IOError as err:
         LOG.error("File '%s' couldn't be opened: '%s'", self.vhdl_file,
                   str(err))
         raise AsFileError(self.vhdl_file, "File could not be opened!")
     if self.errors:
         LOG.error(
             "VHDL analysis function returned with %s error(s).",
             str(len(self.errors)),
         )
         raise AsAnalysisError(
             self.vhdl_file,
             "{} error(s) encountered!".format(len(self.errors)))
def write_config_c(output_path: str):
    """Write the file 'as_config.c', containing the build date
    and version string. Version string is currently not implemented!"""
    LOG.info("Generating as_config.c source file...")
    config_c_name = "as_config.c"
    config_c_template = ('const char *buildVersion = "{version}";\n'
                         'const char *buildDate = "{date}";\n')
    # Fetch and format todays date
    date_string = datetime.today().strftime("%Y-%m-%d")
    version_string = "TEMPORARY"

    try:
        with open(output_path + config_c_name, "w") as file:
            file.write(
                config_c_template.format(date=date_string,
                                         version=version_string))
    except IOError as err:
        LOG.error("Could not write 'as_config.c'! '%s'", str(err))
        raise AsFileError(output_path + config_c_name,
                          detail=str(err),
                          msg="Could not write to file")
예제 #8
0
 def __get_module_scripts_in_dir__(cls, module_dir: str) -> Sequence[str]:
     scripts = []
     module_dir = os.path.realpath(module_dir)
     try:
         mod_folders = os.listdir(append_to_path(module_dir, ""))
     except IOError:
         LOG.error("Could not find module repository folder '%s'!",
                   module_dir)
         raise AsFileError(
             "Could not find module repository folder {}!".format(
                 module_dir))
     for folder in mod_folders:
         folder_path = append_to_path(module_dir, folder)
         if not os.path.isdir(folder_path):
             continue
         script_path = append_to_path(folder_path, cls.SCRIPT_FOLDER)
         if not os.path.isdir(script_path):
             continue
         for file in os.listdir(script_path):
             if cls.__script_name_valid__(file):
                 scripts.append((folder_path, script_path + file))
     return scripts
예제 #9
0
def gather_hw_files(chain: AsProcessingChain,
                    output_folder: str,
                    use_symlinks: bool = True) -> bool:
    """! @brief Copy or link to module VHDL files of an ASTERICS chain.
    Collect all required hardware descriptive files
    for the current ASTERICS system. Mainly looks at the AsModule.files,
    .module_dir and .dependencies attributes to find required files.
    @param chain: current processing chain
    @param output_folder: The root of the output folder structure
    @param use_symlinks: Whether or not to link (True) or copy (False) files
    @return  True on success, else False"""

    LOG.info("Gathering HDL source files...")
    out_path = os.path.realpath(output_folder)

    # Collect all module entity names
    unique_modules = get_unique_modules(chain)

    for entity in unique_modules:
        this_path = append_to_path(out_path, entity)
        try:
            os.makedirs(this_path, 0o755, exist_ok=True)
        except FileExistsError:
            pass
        module = chain.library.get_module_template(entity)
        if module is None:
            continue
        module_dir = os.path.realpath(module.module_dir)
        for hw_file in module.files:
            source = os.path.realpath(hw_file)
            if not hw_file.startswith("/"):
                comp = os.path.commonprefix([module_dir, source])
                if comp != module_dir:
                    # Append the file path to the module directory, cutting off '/'
                    source = append_to_path(module_dir,
                                            hw_file,
                                            add_trailing_slash=False)
            if not os.path.isfile(source):
                raise AsFileError(source, "File not found!")
            filename = source.rsplit("/", maxsplit=1)[-1]
            dest = this_path + filename

            LOG.debug("Gather HW files: Link '%s' to '%s'", source, dest)

            if use_symlinks:
                # Try to create a symlink
                try:
                    os.symlink(source, dest)
                except FileExistsError:
                    # If a symlink already exists, delete it and retry
                    os.unlink(dest)
                    try:
                        os.symlink(source, dest)
                    except IOError as err:
                        LOG.critical(
                            ("Could not link file '%s' of module '%s'!"
                             " - '%s'"),
                            filename,
                            entity,
                            str(err),
                        )
                        return False
                except IOError as err:
                    LOG.critical(
                        ("Could not link file '%s' of module '%s'! "
                         "- '%s'"),
                        filename,
                        entity,
                        str(err),
                    )
                    return False
            else:
                try:
                    copy(source, dest)
                except IOError as err:
                    LOG.critical(
                        ("Could not copy file '%s' of module '%s'!"
                         " - '%s'"),
                        filename,
                        entity,
                        str(err),
                    )
                    return False
    return True
예제 #10
0
def write_vivado_package_tcl(chain: AsProcessingChain,
                             output_path: str,
                             additions_c1: str = "") -> bool:
    """! @brief Write two TCL script fragments sourced by the packaging TCL script.
    This function generates Vivado-specific TCL commands!
    The first fragment is sourced very early in the packaging script and
    contains basic information like the project directory,
    source file directory, etc.
    The second fragment contains packaging commands setting up the
    AXI interface configuration.
    This function needs to access the toplevel modules in the current
    processing chain to generate the TCL code.

    Parameters:
    chain - AsProcessingChain: The current processing chain to build
    output_path - String: Toplevel folder of the current project
    Returns a boolean value: True on success, False otherwise"""

    # Class to manage the AXI interface information for the TCL packaging
    # script
    class TCL_AXI_Interface:
        """'Private' class capsuling methods to write the TCL fragments
        required for the packaging script for Xilinx targets."""
        def __init__(self, axi_type: str):
            # Type of AXI interface ("AXI Slave" / "AXI Master")
            self.axi_type = axi_type
            self.refname = ""  # Variable name of this interface
            self.bus_if_name = ""  # Name of this bus interface in Vivado TCL
            self.clock_name = "_aclk"  # Name of the associated clock
            self.reset_name = "_aresetn"  # Name of the associated reset
            self.if_type = ""  # AXI slave register address block name (Slave)
            self.memory_range = 0  # Range of the address block (Slave)

        def update(self):
            """Update the clock and reset names
            after setting the bus interface name"""
            self.clock_name = self.bus_if_name + self.clock_name
            self.reset_name = self.bus_if_name + self.reset_name

        def get_tcl_bus_assoc_commands(self) -> str:
            """Generate the bus interface association TCL commands"""
            return BUS_IF_ASSOCIATION_TCL_TEMPLATE.format(
                busif=self.bus_if_name,
                clk=self.clock_name,
                rst=self.reset_name,
                ref=self.refname,
            )

        def get_tcl_mem_commands(self) -> str:
            """Generate the memory mapping TCL commands"""
            if any((not self.if_type, not self.memory_range)):
                LOG.debug(
                    ("Generating TCL packaging script: Interface type "
                     "of slave memory range not set for '%s'!"),
                    self.bus_if_name,
                )
                return ""
            # Else ->
            return MEMORY_MAP_TCL_TEMPLATE.format(
                ref=self.refname,
                axi_type=self.if_type,
                mem_range=self.memory_range,
                busif=self.bus_if_name,
                mem_map_ref=self.refname + "_mem_ref",
            )

    LOG.info("Generating TCL packaging scripts...")
    # Generate the necessary paths:
    # IP-Core project directory
    project_dir = append_to_path(os.path.realpath(output_path), "/")
    # Subdirectory containing the HDL sources
    hdl_dir = append_to_path(project_dir, "/hw/hdl/vhdl/")

    # Populate the fragment files with a generic file header
    content1 = TCL_HEADER.format(
        TCL1_NAME, "TCL fragment (1) part of the IP packaging TCL script")
    content2 = TCL_HEADER.format(
        TCL2_NAME, "TCL fragment (2) part of the IP packaging TCL script")
    content3 = TCL_HEADER.format(
        TCL3_NAME, "TCL fragment (2) part of the IP packaging TCL script")

    # Generate the commands for the first basic fragment
    content1 += "\nset projdir " + project_dir
    content1 += "\nset hdldir " + hdl_dir
    if additions_c1:
        content1 += "\n" + additions_c1 + "\n"

    # Possible AXI interface types
    templates = ("AXI_Slave", "AXI_Master")

    for inter in chain.top.interfaces:
        temp = None
        if inter.type.lower() in ("axi_slave_external", "axi_master_external"):
            slave = templates[0] in inter.type
            if slave:
                # For AXI slaves: Populate the TCL AXI class
                temp = TCL_AXI_Interface(templates[0])
                # And set parameters for the memory map (slave registers)
                temp.memory_range = 65536
                temp.if_type = "axi_lite"
            else:
                temp = TCL_AXI_Interface(templates[1])
            temp.refname = (minimize_name(inter.name_prefix).replace(
                temp.axi_type.lower(), "").strip("_"))
            temp.bus_if_name = inter.ports[0].code_name.rsplit("_", 1)[0]

            # Set the reference name and update the clock and reset names
            temp.update()
            # Generate the bus association commands for all interfaces
            content2 += temp.get_tcl_bus_assoc_commands()
            if slave:
                # Only slaves have a memory map
                content2 += temp.get_tcl_mem_commands()

    # Build interface instantiation strings for as_iic modules
    iic_if_inst_str = ["\n"]
    for module in chain.modules:
        if module.entity_name == "as_iic":
            if_name = module.name
            top_if = chain.top.__get_interface_by_un_fuzzy__(
                module.get_interface("in",
                                     if_type="iic_interface").unique_name)
            if top_if is None:
                LOG.error(
                    ("Was not able to determine port names for IIC "
                     "interface '%s' - IIC interface not found!"),
                    if_name,
                )
                mod_prefix = ""
            else:
                mod_prefix = minimize_name(
                    top_if.name_prefix,
                    chain.NAME_FRAGMENTS_REMOVED_ON_TOPLEVEL)
            iic_if_inst_str.append(
                "#Instantiate interface for {}\n".format(if_name))
            iic_if_inst_str.append(
                AS_IIC_MAP_TCL_TEMPLATE.format(iic_signal_prefix=mod_prefix,
                                               iic_if_name=if_name))
            iic_if_inst_str.append(
                "# END Instantiate interface for {}\n\n".format(if_name))
    # Assemble the IIC interface string and add to the TCL fragment
    content2 += "\n".join(iic_if_inst_str)

    tcl_ooc_remove_outsourced_template = "set_property is_enabled false -quiet [get_files -quiet -of_objects [get_filesets sources_1] {{{files}}}]\n"

    outsourced_files = []
    ooc_modules = [
        mod for mod in chain.modules if isinstance(mod, AsModuleWrapper)
    ]
    if ooc_modules:
        tcl_ooc_commands = [
            "# Create OOC blocks\n",
            'puts "Generating Out-of-Context Synthesis Runs..."\n',
        ]
    else:
        tcl_ooc_commands = []
    count = 1
    for mod in ooc_modules:
        source_files = set()
        modfilename = mod.name + ".vhd"
        source_files.add(modfilename)
        outsourced_files.append(modfilename)
        source_files.update(mod.modules[0].files)
        dep_mods = []
        get_dependencies(chain, mod.modules[0], dep_mods)
        if dep_mods:
            for dmod in dep_mods:
                modtemplate = chain.library.get_module_template(dmod)
                source_files.update(modtemplate.files)
        source_files = (sf.rsplit("/", maxsplit=1)[-1]
                        for sf in sorted(source_files))
        tcl_ooc_commands.append(
            TCL_OOC_TEMPLATE.format(
                ent_name=mod.entity_name,
                source_files=" ".join(source_files),
                top_source=mod.name + ".vhd",
                progress=str(count) + " of " + str(len(ooc_modules)),
            ))
        count += 1
    tcl_ooc_commands.append(
        tcl_ooc_remove_outsourced_template.format(
            files=" ".join(outsourced_files)))

    content3 += "\n".join(tcl_ooc_commands)

    # Make sure all files are newline-terminated
    content1 += "\n"
    content2 += "\n"
    content3 += "\n"

    for name, content in zip((TCL1_NAME, TCL2_NAME, TCL3_NAME),
                             (content1, content2, content3)):
        try:
            with open(project_dir + name, "w") as file:
                file.write(content)
        except IOError as err:
            LOG.error("Could not write '%s' in '%s'.", name, project_dir)
            raise AsFileError(project_dir + name, "Could not write file!",
                              str(err))
예제 #11
0
def write_asterics_h(chain: AsProcessingChain, output_path: str):
    """! @brief Write the toplevel ASTERICS driver C header
    The header contains include statements for all driver header files
    and the definition of the register ranges and base addresses."""

    LOG.info("Generating ASTERICS main software driver header file...")
    asterics_h_path = append_to_path(output_path, "/")
    include_list = set()
    include_str = ""
    include_template = '#include "{}" \n'

    module_base_reg_template = "#define AS_MODULE_BASEREG_{modname} {offset}\n"
    module_base_addr_template = (
        "#define AS_MODULE_BASEADDR_{modname} ((uint32_t*)(ASTERICS_BASEADDR + "
        "(AS_MODULE_BASEREG_{modname} * 4 * AS_REGISTERS_PER_MODULE)))\n")

    # Build a list of module additions to asterics.h
    # Additions should be unique
    # (two HW instances of an ASTERICS module use the same driver)
    module_additions = set()
    for module in chain.modules:
        module_additions.update(module.get_software_additions())
    # Format module additions: One line per additional string
    module_additions = "\n".join(module_additions)
    module_additions = (
        "/************************** Module Defines and "
        "Additions ***************************/\n\n") + module_additions

    # Get list of modules
    unique_modules = get_unique_modules(chain)

    # Collect driver header files that need to be included
    def add_header(mod: AsModule):
        for driver in mod.driver_files:
            if driver.endswith(".h"):
                include_list.add(driver.rsplit("/", maxsplit=1)[-1])

    for mod in list(ittls.chain(chain.modules, chain.pipelines)):
        add_header(mod)
    for entity in unique_modules:
        mod = chain.library.get_module_template(entity)
        if mod is not None:
            add_header(mod)
    # Build include list
    include_str = "".join(
        [include_template.format(i) for i in sorted(include_list)])

    # Register base definition list and register range order
    reg_bases = ""
    reg_addrs = ""
    # Generate register definitions
    for addr in chain.address_space:
        # Get register interface object
        regif = chain.address_space[addr]
        # Build register interface module name
        regif_modname = regif.parent.name
        if regif.name_suffix:
            regif_modname += regif.name_suffix
        reg_bases += module_base_reg_template.format(
            modname=regif_modname.upper(), offset=regif.regif_num)
        reg_addrs += module_base_addr_template.format(
            modname=regif_modname.upper())

    try:
        with open(asterics_h_path + ASTERICS_H_NAME, "w") as file:
            # Write asterics.h
            file.write(
                ASTERICS_H_TEMPLATE.format(
                    header=ASTERICS_HEADER_SW.format(
                        filename=ASTERICS_H_NAME,
                        description=ASTERICS_H_DESCRIPTION,
                    ),
                    base_addr=chain.asterics_base_addr,
                    regs_per_mod=chain.max_regs_per_module,
                    module_driver_includes=include_str,
                    base_regs=reg_bases,
                    addr_map=reg_addrs,
                    module_additions=module_additions,
                ))

    except IOError as err:
        print("Couldn't write {}: '{}'".format(
            asterics_h_path + ASTERICS_H_NAME, err))
        raise AsFileError(
            asterics_h_path + ASTERICS_H_NAME,
            detail=str(err),
            msg="Could not write to file",
        )
예제 #12
0
def add_vears_core(
    output_path: str,
    asterics_path: str,
    use_symlinks: bool = True,
    force: bool = False,
):
    """! @brief Link or copy the VEARS IP-Core.
    VEARS is copied/linked from the ASTERICS installation to the output path.
    @param output_path: Directory to link/copy VEARS to.
    @param asterics_path: Toplevel folder of the ASTERICS installation.
    @param use_symlinks: If True, VEARS will be linked, else copied.
    @param force: If True, the link or folder will be deleted if already present."""
    vears_path = append_to_path(asterics_path, VEARS_REL_PATH)
    vears_path = os.path.realpath(vears_path)
    target = append_to_path(output_path, "VEARS", add_trailing_slash=False)

    if force and os.path.exists(target):
        if os.path.islink(target) or os.path.isfile(target):
            try:
                os.remove(target)
            except IOError as err:
                LOG.error("Could not remove file '%s'! VEARS not added!",
                          target)
                raise AsFileError(
                    target,
                    "Could not remove file to link/copy VEARS!",
                    str(err),
                )
        else:
            try:
                rmtree(target)
            except IOError as err:
                LOG.error("Could not remove folder '%s'! VEARS not added!",
                          target)
                raise AsFileError(
                    target,
                    "Could not remove folder to link/copy VEARS!",
                    str(err),
                )

    if use_symlinks:
        if not os.path.exists(output_path):
            try:
                os.makedirs(output_path, mode=0o755, exist_ok=True)
            except IOError as err:
                LOG.error(
                    "Could not create directory for VEARS: '%s'! - '%s'",
                    output_path,
                    str(err),
                )
                raise AsFileError(
                    output_path,
                    "Could not create directory for VEARS!",
                    str(err),
                )
        try:
            target = os.path.realpath(target)
            os.symlink(vears_path, target, target_is_directory=True)
        except IOError as err:
            LOG.error("Could not create a link to the VEARS IP-Core! - '%s'",
                      str(err))
            raise AsFileError(target, "Could not create link to VEARS!",
                              str(err))
    else:
        try:
            os.makedirs(target, mode=0o755, exist_ok=True)
            target = os.path.realpath(target)
            copytree(vears_path, target)
        except IOError as err:
            LOG.error("Could not copy the VEARS IP-Core!")
            raise AsFileError(output_path, "Could not copy VEARS!", str(err))
def write_vivado_package_tcl(chain: AsProcessingChain,
                             output_path: str,
                             additions_c1: str = "") -> bool:
    """Write two TCL script fragments sourced by the packaging TCL script.
    This function generates Vivado-specific TCL commands!
    The first fragment is sourced very early in the packaging script and
    contains basic information like the project directory,
    source file directory, etc.
    The second fragment contains packaging commands setting up the
    AXI interface configuration.
    This function needs to access the toplevel modules in the current
    processing chain to generate the TCL code.

    Parameters:
    chain - AsProcessingChain: The current processing chain to build
    output_path - String: Toplevel folder of the current project
    Returns a boolean value: True on success, False otherwise"""

    as_iic_map_tcl_template = (
        "# Set up interface properties:\n"
        "ipx::add_bus_interface {iic_if_name} [ipx::current_core]\n"
        "set_property abstraction_type_vlnv xilinx.com:interface:iic_rtl:1.0 "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]\n"
        "set_property bus_type_vlnv xilinx.com:interface:iic:1.0 "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]\n"
        "set_property interface_mode master "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]\n"
        "set_property display_name asterics_{iic_if_name} "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]\n"
        "# IIC port mapping:\n"
        "ipx::add_port_map SCL_T [ipx::get_bus_interfaces {iic_if_name} "
        "-of_objects [ipx::current_core]]\n"
        "set_property physical_name {iic_signal_prefix}scl_out_enable "
        "[ipx::get_port_maps SCL_T -of_objects "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]]\n"
        "ipx::add_port_map SDA_O [ipx::get_bus_interfaces {iic_if_name} "
        "-of_objects [ipx::current_core]]\n"
        "set_property physical_name {iic_signal_prefix}sda_out "
        "[ipx::get_port_maps SDA_O -of_objects "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]]\n"
        "ipx::add_port_map SDA_I [ipx::get_bus_interfaces {iic_if_name} "
        "-of_objects [ipx::current_core]]\n"
        "set_property physical_name {iic_signal_prefix}sda_in "
        "[ipx::get_port_maps SDA_I -of_objects "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]]\n"
        "ipx::add_port_map SDA_T [ipx::get_bus_interfaces {iic_if_name} "
        "-of_objects [ipx::current_core]]\n"
        "set_property physical_name {iic_signal_prefix}sda_out_enable "
        "[ipx::get_port_maps SDA_T -of_objects "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]]\n"
        "ipx::add_port_map SCL_O [ipx::get_bus_interfaces {iic_if_name}"
        " -of_objects [ipx::current_core]]\n"
        "set_property physical_name {iic_signal_prefix}scl_out "
        "[ipx::get_port_maps SCL_O -of_objects "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]]\n"
        "ipx::add_port_map SCL_I [ipx::get_bus_interfaces {iic_if_name} "
        "-of_objects [ipx::current_core]]\n"
        "set_property physical_name {iic_signal_prefix}scl_in "
        "[ipx::get_port_maps SCL_I -of_objects "
        "[ipx::get_bus_interfaces {iic_if_name} -of_objects [ipx::current_core]]]\n"
    )

    # Class to manage the AXI interface information for the TCL packaging
    # script
    class TCL_AXI_Interface:
        """'Private' class capsuling methods to write the TCL fragments
        required for the packaging script for Xilinx targets."""

        # TCL command templates:
        bus_if_association_tcl_template = (
            "ipx::associate_bus_interfaces -clock {clk} -busif {busif} -clear "
            "[ipx::current_core]\n"
            "# ^ Dissassociate any signals with this AXI interface"
            " (to be save)\n"
            "# Associate the correct clock and reset signals\n"
            "ipx::associate_bus_interfaces -clock {clk} -reset {rst} "
            "-busif {busif} [ipx::current_core]\n"
            "# Store the interface object in a variable with known name\n"
            "set {ref} [ipx::get_bus_interfaces -of_objects "
            "[ipx::current_core] {busif}]\n")
        memory_map_tcl_template = (
            "ipx::remove_memory_map slave_s_axi [ipx::current_core]\n"
            "ipx::remove_memory_map {ref} [ipx::current_core]\n"
            "# ^ Remove any memory maps from the interface\n"
            "# (potentially) Re-add a memory map\n"
            "ipx::add_memory_map {mem_map_ref} [ipx::current_core]\n"
            "# Add a slave memory map reference\n"
            "set_property slave_memory_map_ref {mem_map_ref} ${ref}\n"
            "# Add an address block to the memory map "
            "using the above reference\n"
            "ipx::add_address_block {{{axi_type}}} [ipx::get_memory_maps "
            "{mem_map_ref} -of_objects [ipx::current_core]]\n"
            "# Set the address block range\n"
            "set_property range {{{mem_range}}} [ipx::get_address_blocks "
            "{{{axi_type}}} -of_objects [ipx::get_memory_maps "
            "{{{mem_map_ref}}}  -of_objects [ipx::current_core]]]\n")

        def __init__(self, axi_type: str):
            # Type of AXI interface ("AXI Slave" / "AXI Master")
            self.axi_type = axi_type
            self.refname = ""  # Variable name of this interface
            self.bus_if_name = ""  # Name of this bus interface in Vivado TCL
            self.clock_name = "_aclk"  # Name of the associated clock
            self.reset_name = "_aresetn"  # Name of the associated reset
            self.if_type = ""  # AXI slave register address block name (Slave)
            self.memory_range = 0  # Range of the address block (Slave)

        def update(self):
            """Update the clock and reset names
            after setting the bus interface name"""
            self.clock_name = self.bus_if_name + self.clock_name
            self.reset_name = self.bus_if_name + self.reset_name

        def get_tcl_bus_assoc_commands(self) -> str:
            """Generate the bus interface association TCL commands"""
            return self.bus_if_association_tcl_template.format(
                busif=self.bus_if_name,
                clk=self.clock_name,
                rst=self.reset_name,
                ref=self.refname,
            )

        def get_tcl_mem_commands(self) -> str:
            """Generate the memory mapping TCL commands"""
            if any((not self.if_type, not self.memory_range)):
                LOG.debug(
                    ("Generating TCL packaging script: Interface type "
                     "of slave memory range not set for '%s'!"),
                    self.bus_if_name,
                )
                return ""
            # Else ->
            return self.memory_map_tcl_template.format(
                ref=self.refname,
                axi_type=self.if_type,
                mem_range=self.memory_range,
                busif=self.bus_if_name,
                mem_map_ref=self.refname + "_mem_ref",
            )

    LOG.info("Generating TCL packaging scripts...")
    # Generate the necessary paths:
    # IP-Core project directory
    project_dir = append_to_path(os.path.realpath(output_path), "/")
    # Subdirectory containing the HDL sources
    hdl_dir = append_to_path(project_dir, "/hw/hdl/vhdl/")

    # Populate the fragment files with a generic file header
    content1 = TCL_HEADER.format(
        TCL1_NAME, "TCL fragment (1) part of the IP packaging TCL script")
    content2 = TCL_HEADER.format(
        TCL2_NAME, "TCL fragment (2) part of the IP packaging TCL script")

    # Generate the commands for the first basic fragment
    content1 += "\nset projdir " + project_dir
    content1 += "\nset hdldir " + hdl_dir
    if additions_c1:
        content1 += "\n" + additions_c1 + "\n"

    # Possible AXI interface types
    templates = ("AXI_Slave", "AXI_Master")

    for inter in chain.top.interfaces:
        temp = None
        if inter.type.lower() in ("axi_slave_external", "axi_master_external"):
            slave = templates[0] in inter.type
            if slave:
                # For AXI slaves: Populate the TCL AXI class
                temp = TCL_AXI_Interface(templates[0])
                # And set parameters for the memory map (slave registers)
                temp.memory_range = 65536
                temp.if_type = "axi_lite"
            else:
                temp = TCL_AXI_Interface(templates[1])
            temp.refname = (as_help.minimize_name(inter.name_prefix).replace(
                temp.axi_type.lower(), "").strip("_"))
            temp.bus_if_name = inter.ports[0].code_name.rsplit("_", 1)[0]

            # Set the reference name and update the clock and reset names
            temp.update()
            # Generate the bus association commands for all interfaces
            content2 += temp.get_tcl_bus_assoc_commands()
            if slave:
                # Only slaves have a memory map
                content2 += temp.get_tcl_mem_commands()

    # Build interface instantiation strings for as_iic modules
    iic_if_inst_str = ["\n"]
    for module in chain.modules:
        if module.entity_name == "as_iic":
            if_name = module.name
            top_if = chain.top.__get_interface_by_un_fuzzy__(
                module.get_interface("in",
                                     if_type="iic_interface").unique_name)
            if top_if is None:
                LOG.error(
                    ("Was not able to determine port names for IIC "
                     "interface '%s' - IIC interface not found!"),
                    if_name,
                )
                mod_prefix = ""
            else:
                mod_prefix = as_help.minimize_name(
                    top_if.name_prefix,
                    chain.NAME_FRAGMENTS_REMOVED_ON_TOPLEVEL)
            iic_if_inst_str.append(
                "#Instantiate interface for {}\n".format(if_name))
            iic_if_inst_str.append(
                as_iic_map_tcl_template.format(iic_signal_prefix=mod_prefix,
                                               iic_if_name=if_name))
            iic_if_inst_str.append(
                "# END Instantiate interface for {}\n\n".format(if_name))
    # Assemble the IIC interface string and add to the TCL fragment
    content2 += "\n".join(iic_if_inst_str)

    # Make sure both files are newline-terminated
    content1 += "\n"
    content2 += "\n"

    # Write fragment 1
    try:
        with open(project_dir + TCL1_NAME, "w") as file:
            file.write(content1)
    except IOError as err:
        LOG.error("Could not write '%s' in '%s'.", TCL1_NAME, project_dir)
        raise AsFileError(project_dir + TCL1_NAME, "Could not write file!",
                          str(err))

    # Write fragment 2
    try:
        with open(project_dir + TCL2_NAME, "w") as file:
            file.write(content2)
    except IOError as err:
        LOG.error("Could not write '%s' in '%s'.", TCL2_NAME, project_dir)
        raise AsFileError(project_dir + TCL2_NAME, "Could not write file!",
                          str(err))
def write_asterics_h(chain: AsProcessingChain, output_path: str):
    """Write the toplevel ASTERICS driver header containing include
    statements for all driver header files and the definition of the register
    ranges and base addresses."""
    LOG.info("Generating ASTERICS main software driver header file...")
    asterics_h_path = append_to_path(output_path, "/")
    include_list = ""
    include_template = '#include "{}" \n'

    module_base_reg_template = "#define AS_MODULE_BASEREG_{modname} {offset}\n"
    module_base_addr_template = (
        "#define AS_MODULE_BASEADDR_{modname} ((uint32_t*)(ASTERICS_BASEADDR + "
        "(AS_MODULE_BASEREG_{modname} * 4 * AS_REGISTERS_PER_MODULE)))\n")

    # Build a list of module additions to asterics.h
    # Additions should be unique
    # (two HW instances of an ASTERICS module use the same driver)
    module_additions = set()
    for module in chain.modules:
        module_additions.update(module.get_software_additions())
    # Format module additions: One line per additional string
    module_additions = "\n".join(module_additions)
    module_additions = (
        "/************************** Module Defines and "
        "Additions ***************************/\n\n") + module_additions

    # Get list of modules
    unique_modules = get_unique_modules(chain)

    # Collect driver header files that need to be included
    for entity in unique_modules:
        module = chain.library.get_module_template(entity)
        driver_path = module.module_dir + "software/driver/"
        if not os.path.isdir(driver_path):
            continue
        for file in os.listdir(driver_path):
            if file[-2:] == ".h":
                if not any(file in include for include in include_list):
                    include_list += include_template.format(file)

    # Register base definition list and register range order
    reg_bases = ""
    reg_addrs = ""
    # Generate register definitions
    for addr in chain.address_space:
        # Get register interface object
        regif = chain.address_space[addr]
        # Build register interface module name
        regif_modname = regif.parent.name
        if regif.name_suffix:
            regif_modname += regif.name_suffix
        reg_bases += module_base_reg_template.format(
            modname=regif_modname.upper(), offset=regif.regif_num)
        reg_addrs += module_base_addr_template.format(
            modname=regif_modname.upper())

    try:
        with open(asterics_h_path + ASTERICS_H_NAME, "w") as file:
            # Write asterics.h
            file.write(
                ASTERICS_H_TEMPLATE.format(
                    base_addr=chain.asterics_base_addr,
                    regs_per_mod=chain.max_regs_per_module,
                    module_driver_includes=include_list,
                    base_regs=reg_bases,
                    addr_map=reg_addrs,
                    module_additions=module_additions,
                ))

    except IOError as err:
        print("Couldn't write {}: '{}'".format(
            asterics_h_path + ASTERICS_H_NAME, err))
        raise AsFileError(
            asterics_h_path + ASTERICS_H_NAME,
            detail=str(err),
            msg="Could not write to file",
        )