예제 #1
0
def manage_data_widths(source: Port, sink: Port) -> bool:
    """This function checks if the data widths of both ports are compatible.
    It resolves the data widths by replacing the generics with their respective
    values and compares the resolved versions. The port objects aren't modified.
    Exception: For toplevel modules an automatic propagation of generics is
    attempted if the specific case is clear cut.
    Parameters:
    source and sink: Both Port objects to compare and adjust.
    Returns: Boolean value: True on success, else False.
    """
    # Determine data flow direction
    if source.get_direction_normalized() == "in":
        pin = source
        pout = sink
    else:
        pin = sink
        pout = source

    # Get "state" of data widths (are they fixed?)
    pin_res = is_data_width_resolved(pin.data_width)
    pout_res = is_data_width_resolved(pout.data_width)

    # If fixed and matching -> OK
    if pin_res and pout_res:
        if pin.data_width == pout.data_width:
            return True

    LOG.debug(
        "Data widths of '%s' and '%s' not resolved, resolving...",
        pin.code_name,
        pout.code_name,
    )
    # Evaluate data widths
    pin_rdw = resolve_data_width(pin)
    pout_rdw = resolve_data_width(pout)

    # Do they match now? Yes -> OK
    if pin_rdw == pout_rdw:
        LOG.debug("Data widths resolved successfully!")
        return True
    # Else: Warn user; automatic adjustment requires more development!
    # The simple "beta" version caused more problems than it was worth...

    # For toplevel modules (AXI Master / Slave):
    # Attempt automatic management of the generics in very simple cases
    # as it is inconvenient for the user to manually resolve toplevel
    # problems (especially for auto-instantiated modules like AXI_Masters)
    pin_mod = AsModule.get_parent_module(pin)
    pout_mod = AsModule.get_parent_module(pout)
    # If either port's module is directly instantiated in toplevel
    if (pin_mod.modlevel < 2) or (pout_mod.modlevel < 2):
        igen = pin.generics
        ogen = pout.generics

        # If both ports have exactly one generic
        if len(igen) == 1 and len(ogen) == 1:
            igen = igen[0]
            ogen = ogen[0]
            # Store current values
            igen_v = igen.value
            ogen_v = ogen.value
            # Propagate the source generic to the sink port
            igen.value = ogen
            pin_rdw = resolve_data_width(pin)
            # Did that resolve the difference?
            if pin_rdw == pout_rdw:
                LOG.debug(
                    ("Generic Auto-Propagation: For ports '%s' and "
                     "'%s' of modules '%s' and '%s': Set value of "
                     "generic '%s' to generic '%s' of module '%s'."),
                    pout.code_name,
                    pin.code_name,
                    pin_mod.name,
                    pout_mod.name,
                    igen.code_name,
                    ogen.code_name,
                    pout_mod.name,
                )
                return True
            # If not, reverse the propagation and try the other way around
            igen.value = igen_v
            ogen.value = igen
            pin_rdw = resolve_data_width(pin)
            pout_rdw = resolve_data_width(pout)
            # Did that work?
            if pin_rdw == pout_rdw:
                LOG.debug(
                    ("Generic Auto-Propagation: For ports '%s' and "
                     "'%s' of modules '%s' and '%s': Set value of "
                     "generic '%s' to generic '%s' of module '%s'."),
                    pout.code_name,
                    pin.code_name,
                    pin_mod.name,
                    pout_mod.name,
                    ogen.code_name,
                    igen.code_name,
                    pin_mod.name,
                )
                return True
            # If not, reverse the propagation and print the user warning
            ogen.value = ogen_v
            pout_rdw = resolve_data_width(pout)
        # Gather some data to generate a useful user message
        # Involved Generic names
        all_gens = []
        all_gens.extend(pout.generics)
        all_gens.extend(pin.generics)
        all_gens = [gen.code_name for gen in all_gens]
        # Module names
        pout_mod = pout_mod.name
        pin_mod = pin_mod.name
        # Data width strings
        pout_dw = Port.data_width_to_string(pout.data_width)
        pout_rdw = Port.data_width_to_string(pout_rdw)
        pin_dw = Port.data_width_to_string(pin.data_width)
        pin_rdw = Port.data_width_to_string(pin_rdw)

        # User warning message
        LOG.error(
            ("Data widths between ports '%s' and '%s' of modules "
             "'%s' and '%s' differ and must be adjusted manually!\n"
             "Data widths: For port '%s': (%s) - resolved as (%s) | "
             "For port '%s': (%s) - resolved as (%s).\n"
             "Use generics '%s' of the respective modules."),
            pout.code_name,
            pin.code_name,
            pout_mod,
            pin_mod,
            pout.code_name,
            pout_dw,
            pout_rdw,
            pin.code_name,
            pin_dw,
            pin_rdw,
            all_gens,
        )

    # Special case for "slv_reg_interface" connections to as_regmgr
    if (pin.parent.type == "slv_reg_interface") and (pout.parent.type
                                                     == "slv_reg_interface"):
        if pin_mod.entity_name == "as_regmgr":
            pin.data_width = copy(pout.data_width)
            return True
        if pout_mod.entity_name == "as_regmgr":
            pout.data_width = copy(pin.data_width)
            return True

    return False
    def __instantiate_module__(self, module: AsModule) -> Sequence[str]:
        """Generate VHDL code as a list of strings to instantiate 'module'.
        Handles generic assignment and port mapping."""
        out = [
            "\n  -- Instantiate module {}:\n".format(module.name),
            "  {} : entity {}\n".format(
                "as_main_impl" if module.name == "as_main" else module.name,
                module.entity_name,
            ),
        ]
        gen_str = []
        if module.generics:
            gen_str.append("  generic map(\n")
            for tgen in module.generics:
                # If the generic was set by the user in the generator,
                # use that value
                if isinstance(tgen.value, Generic):
                    ret = tgen.value.code_name
                else:
                    ret = self.__get_printable_generic_value__(tgen)
                    # Skip generics that use the default value
                    if ret == tgen.default_value:
                        continue
                gen_str.append("    {} => {},\n".format(tgen.code_name, ret))
        if len(gen_str) > 1:
            # Replace the comma in the last generic mapping with a brace ')'
            gen_str[-1] = ")".join(gen_str[-1].rsplit(",", maxsplit=1))
            out.extend(gen_str)
        out.append("  port map(\n")

        full_port_list = module.get_full_port_list(include_signals=False)
        # For every port of this module:
        for port in full_port_list:
            # Target of the port map
            target = None

            # Determine the format for this ports port map line
            if full_port_list.index(port) < len(full_port_list) - 1:
                templ_str = "    {} => {},\n"
            else:
                templ_str = "    {} => {});\n"

            # Port mapping target is the port's glue signal
            if port.glue_signal and isinstance(port.glue_signal, GlueSignal):
                glue = port.glue_signal
                target = glue.code_name

                # If this glue signal should be included in the signal list
                if glue.is_signal:
                    # Add the glue signal to the signal list
                    # Assemble vhdl signal declaration string
                    glue_signal_str = "  signal {} : {};\n".format(
                        glue.code_name, as_help.get_printable_datatype(glue)
                    )
                    # Make sure the same glue signal is not declared twice
                    if glue_signal_str not in self.signal_list:
                        self.signal_list.append(glue_signal_str)
            else:  # If no glue signal present:
                # Port mapping target is one of the connected ports,
                # depending on port direction
                target = (
                    port.incoming
                    if port.get_direction_normalized() == "in"
                    else port.outgoing[0]
                    if port.outgoing
                    else None
                )
                # If the target is a Port object: use the code_name as target
                if isinstance(target, Port):
                    target = target.code_name
                # For strings: use the string as is (eg. for static values)
                elif isinstance(target, str):
                    target = target
                else:
                    target = None

                if not target:
                    # If the target is 'None': get neutral value
                    if port.get_direction_normalized() == "in":
                        target = port.get_neutral_value()
                    else:
                        target = "open"
                    # And warn the user of an unconnected port
                    if port.optional:
                        LOG.debug(
                            (
                                "Optional port '%s' of module '%s' was "
                                "left unconnected, automatically set to [%s]!"
                            ),
                            port.code_name,
                            AsModule.get_parent_module(port).name,
                            target,
                        )
                    else:
                        LOG.info(
                            (
                                "Port '%s' of module '%s' was left "
                                "unconnected, automatically set to [%s]!"
                            ),
                            port.code_name,
                            AsModule.get_parent_module(port).name,
                            target,
                        )
            # Insert the values in the format string and add to the return list
            out.append(templ_str.format(port.code_name, target))
        return out