예제 #1
0
class AsWindowModule(AsModule):
    """! @brief Class representing a module for use in 2D Window Pipelines.
    Based on the AsModule class.
    Adds multiple attributes for managing aspects of the pipeline."""

    standard_port_templates = [
        StandardPort(name="clk", port_type="external"),
        StandardPort(
            "strobe",
            port_type="single",
            extra_rules=[
                Port.Rule("source_present", "fallback_signal(strobe_int)")
            ],
            overwrite_ruleset=True,
        ),
        StandardPort(
            "strobe_in",
            port_type="single",
            extra_rules=[
                Port.Rule("source_present", "fallback_signal(strobe_int)")
            ],
            overwrite_ruleset=True,
        ),
        StandardPort(
            "reset",
            port_type="single",
            extra_rules=[
                Port.Rule("source_present", "fallback_signal(reset_int)")
            ],
            overwrite_ruleset=True,
        ),
        StandardPort(
            name="flush_in",
            port_type="single",
            extra_rules=[Port.Rule("source_present", "fallback_signal(flush)")],
            overwrite_ruleset=True,
        ),
    ]

    def __init__(self):
        super().__init__()
        self.window_interfaces = []  # List of AsWindowInterface objects

        # Strobe delays:
        # Delay from previous module
        self.input_delay = -1
        # Delay from the processing of this module
        self.processing_delay = 0
        # Sum of all delays of this module on its output
        self.delay = 0
        # Additional delay set by user
        self.user_delay = 0

        self.pipe = None  # Reference to the As2DWindowPipeline object (parent)

    def __str__(self) -> str:
        return "ASTERICS Window-Module '{}' ({})".format(
            self.name, self.entity_name
        )

    def __update_delay__(self):
        self.delay = self.input_delay + self.processing_delay + self.user_delay

    ##
    # @addtogroup automatics_cds
    # @{

    def connect(self, connect_to) -> bool:
        """! @brief Add a connection from this module as a data source."""
        self.pipe.connect(self, connect_to)

    def get(self, name: str, direction: str = ""):
        """! @brief Generic interface and port search method.
        This method first searches for matching window interfaces.
        If none were found, passes the search request onto AsModule.get()."""
        inter = self.get_window_interface(name, direction)
        if inter:
            LOG.debug(
                "Selected interface '%s' from '%s'.", inter.name, self.name
            )
            return inter
        return super().get(name, direction)

    def get_window_interface(
        self, interface_name: str, direction: str = ""
    ) -> AsWindowInterface:
        """! @brief Search for and return a 'as_window' interface of this module.
        @param interface_name  Name of the interface to search for
        @param direction  Data direction of the interface to search for
        @return  A window interface or None if no match is found"""
        return next(
            (
                inter
                for inter in self.window_interfaces
                if inter.name == interface_name
                and (
                    True if (not direction) else (inter.direction == direction)
                )
            ),
            None,
        )

    ## @}

    def __gen_uname_for_window_ifs__(self):
        # Generate unique name for this module's interfaces
        for inter in self.window_interfaces:
            inter.unique_name = "{}_{}_{}_{}".format(
                inter.type, inter.name, inter.direction, self.name
            )

    def list_module(self, verbosity: int = 0):
        """! @brief List the configuration of this window module."""
        super().list_module(verbosity)

        if self.window_interfaces:
            print("\n~~~\nWindow Interfaces:")
            for inter in self.window_interfaces:
                if verbosity > 0:
                    inter.print_interface(verbosity > 0)
                else:
                    print(
                        str(inter)
                        + (" ->" if inter.direction == "in" else " <-")
                    )
                print("")

    def get_full_port_list(self, include_signals: bool = True) -> list:
        """Return a list containing a reference to every port in this module"""
        plist = super().get_full_port_list(include_signals)
        plist.extend((w.window_port for w in self.window_interfaces))
        return plist

    ## @ingroup automatics_analyze
    def discover_module(self, file: str):
        """! @brief Analyze and parse a VHDL toplevel file creating an AsWindowModule.
        Passes the call onto AsModule.discover_module() and handles
        additional tasks requried for AsWindowModules."""
        super().discover_module(
            file,
            window_module=True,
            extra_function=self.__assign_window_interfaces__,
        )

    ## @ingroup automatics_analyze
    def __assign_window_interfaces__(self):
        """! @brief Picks out all window ports and bundles them to WindowInterfaces.
        Must be run before regular interfaces are assigned."""

        to_remove = []  # Collecting ports assigned before
        for port in self.entity_ports:
            # If port is not part of a window interface, skip it
            if not getattr(port, "window_config"):
                continue
            # Create a new window interface and add the port
            inter = AsWindowInterface()
            inter.name = port.code_name
            inter.add_port(port)
            inter.window_port = port
            inter.assign_to(self)
            inter.direction = port.direction

            # Register the window interface with the module
            self.window_interfaces.append(inter)
            to_remove.append(port)

        for port in to_remove:
            self.entity_ports.remove(port)
예제 #2
0
class AsWindowModule(AsModule):
    """Automatics class representing a module for the 2D-Window-Pipeline.
    Based on the AsModule class.
    Adds multiple attributes for managing aspects of the pipeline."""

    standard_port_templates = [
        StandardPort(
            "strobe",
            port_type="single",
            extra_rules=[Port.Rule("both_present", "connect")],
        )
    ]
    standard_port_templates.extend(AsModule.standard_port_templates)

    def __init__(self):
        super().__init__()
        self.input_refs = []  # Updated. AsLayerRef objects
        self.output_refs = []  # Updated. AsLayerRef objects
        self.delay = None  # Module output delay in clock cycles
        self.window_interfaces = []  # List of AsWindowInterface objects
        self.input_layers = []  # List of AsLayers this module gets data from
        self.output_layers = []  # List of AsLayers with this module as input
        # ↓ Offset that applies to all outgoing references
        self.offset = WindowRef(0, 0)
        self.user_phys_offset = None  # Physical position set by user
        self.pipe = None  # Reference to the As2DWindowPipeline object (parent)

    def __str__(self) -> str:
        return "ASTERICS Window-Module '{}' ({})".format(
            self.name, self.entity_name)

    def update_input_refs_for_offset(self, reference_layer: AsLayer):
        """'Normalize' the input references depending on the layer offsets.
        To build a common reference space, data inputs from different data
        layers must be shifted to synchronize for the different latencies
        in the pipeline.
        Example:
        Layer 0: Input directly from a camera. Has 0 pixel latency: offset = 0
        Layer 1: Gets data from a Gauss module delivering the first processed
                 pixel after two pixels + an entire row of pixels = offset
        This module uses data from both layers.
        To get the data of the "same" pixel, both raw and from the gauss filter,
        the raw pixel references must be shifted by the difference the offset of
        the layer with the largest offset and each other input layer.
        So: shift_input_refs(for=layer0, by=abs(layer1.offset - layer0.offset))

        This method should run after all connections to this module are
        registered. It MUST be run exactly and only ONCE for every module!"""

        # Get the reference layer (with the largest offset)
        self.input_layers.sort(key=lambda layer: layer.offset, reverse=True)
        reflayer = self.input_layers[0]

        self.input_refs.sort(key=lambda r: r.layer)

        crt_layer = None
        # For every input reference
        for ref in self.input_refs:
            # Inputs of the reference layer must be left unmodified
            if ref.layer is reflayer:
                continue
            # If the layer of the current ref is not the same as last loop
            if ref.layer is not crt_layer:
                crt_layer = ref.layer
                # Calculate the difference in layer offsets
                delta = abs(reflayer.offset - crt_layer.offset)
            ref += delta  # Apply the offset delta (shift the reference)

    def set_physical_position(self, ref_x: int, ref_y: int):
        self.user_phys_offset = WindowRef(ref_x, ref_y)

    def connect(self, interface, layer: AsLayer, offset: tuple = None) -> bool:
        """Add a connection operation to the queue.
        Connect the interface <interface_name> of this module to the data layer
        <layer>. The data flow direction is determined via the data direction
        of the specified interface."""
        if isinstance(interface, str):
            inter = self.get_window_interface(interface)
        elif isinstance(interface, Interface):
            inter = interface
        else:
            inter = None
        if not inter:
            LOG.error("Could not find interface with name '%s'!", interface)
            raise ValueError(
                "Interface '{}' does not exist!".format(interface))
        self.pipe.connect(inter, layer, offset)

    def get_output_layer_offset(self) -> AsLayerRef:
        """Returns the largest offset of any layer that inputs to this module.
        """
        out = AsLayerRef(0, 0, None)
        for ref in self.input_refs:
            if ref.layer.offset > out:
                out.update(ref.layer.offset)
                out.layer = ref.layer
        return out

    def get(self, name: str, direction: str = ""):
        inter = self.get_window_interface(name, direction)
        if inter:
            LOG.debug("Selected interface '%s' from '%s'.", inter.name,
                      self.name)
            return inter
        return super().get(name, direction)

    def get_window_interface(self,
                             interface_name: str,
                             direction: str = "") -> AsWindowInterface:
        """Search for and return a specific WindowInterface of this module."""
        return next(
            (inter for inter in self.window_interfaces
             if inter.name == interface_name and (True if (
                 not direction) else (inter.direction == direction))),
            None,
        )

    def __gen_uname_for_window_ifs__(self):
        # Generate unique name for this module's interfaces
        for inter in self.window_interfaces:
            inter.unique_name = "{}_{}_{}_{}".format(inter.type, inter.name,
                                                     inter.direction,
                                                     self.name)

    def list_module(self, verbosity: int = 0):
        """List the configuration of this window module."""
        super().list_module(verbosity)

        if self.window_interfaces:
            print("\n~~~\nWindow Interfaces:")
            for inter in self.window_interfaces:
                if verbosity > 0:
                    inter.print_interface(verbosity > 0)
                else:
                    print(
                        str(inter) +
                        (" ->" if inter.direction == "in" else " <-"))
                print("")

    def add_input(self, col: int, row: int, layer: AsLayer):
        self.input_refs.append(AsLayerRef(col, row, layer))

    def add_output(self, col: int, row: int, layer: AsLayer):
        self.output_refs.append(AsLayerRef(col, row, layer))

    def add_inputs(self, input_matrix: list, layer: AsLayer):
        for col in range(len(input_matrix)):
            for row in range(len(input_matrix[col])):
                for _ in range(input_matrix[col][row]):
                    self.add_input(col, row, layer)

    def add_outputs(self, output_matrix: list, layer: AsLayer):
        for col in range(len(output_matrix)):
            for row in range(len(output_matrix[col])):
                for _ in range(output_matrix[col][row]):
                    self.add_output(col, row, layer)

    def discover_module(self, file: str):
        """Read and parse VHDL-file 'file'.
           Extracts the generic, port and register interface definitions
           to configure this AsWindowModule object.
           Passes the call onto AsModule.discover_module() and handles
           additional tasks requried for AsWindowModules."""
        super().discover_module(
            file,
            window_module=True,
            extra_function=self.__assign_window_interfaces__)
        for inter in self.window_interfaces:
            inter.update_footprint()

    def __assign_window_interfaces__(self):
        """Must be run before regular interfaces are assigned.
        Picks out all ports with auto-tags and bundles them to WindowInterfaces.
        """

        to_remove = []  # Collecting ports assigned before
        for port in self.entity_ports:
            # If port is not part of a window interface, skip it
            if not getattr(port, "window_config"):
                continue
            # Try to match port with an existing window interface
            match = self.__assign_port_to_existing_window__(port)
            if match:
                to_remove.append(port)
                continue
            match = self.__assign_port_to_new_window__(port)
            if not match:
                LOG.debug("Port '%s' not assigned to a window interface.",
                          port.code_name)
            else:
                to_remove.append(port)

        for port in to_remove:
            self.entity_ports.remove(port)

    def __assign_port_to_existing_window__(self, port: Port) -> bool:
        """Helper function for '__assign_window_interfaces__().
        Matches and adds 'port' to existing window interfaces.
        Returns False if no match is found!"""
        try:  # Just making sure
            # Check each window interface
            for inter in self.window_interfaces:
                # If the layer names match, we can assign the port
                if (port.window_config.layer
                        == inter.layer_name) and (port.direction
                                                  == inter.direction):
                    inter.add_port(port)
                    break  # Done!
            else:
                return False
        except AttributeError as err:
            LOG.debug("'%s': Got Attribute Error: '%s'", str(self), str(err))
            return False

        LOG.debug("Port '%s' assigned to interface '%s'", port.code_name,
                  str(inter))
        return True

    def __assign_port_to_new_window__(self, port: Port) -> bool:
        """Helper function for __assign_window_interfaces__().
        Creates a new AsWindowInterface instance based on 'port'."""
        try:
            # If we have an interface name...
            if port.window_config.intername:
                # Create a new window interface and add the port
                inter = AsWindowInterface()
                inter.name = port.window_config.intername
                inter.add_port(port)
                inter.assign_to(self)
                inter.direction = port.direction

                # Register the window interface with the module
                self.window_interfaces.append(inter)
            else:
                LOG.error(
                    "Missing interface name for port '%s' of module '%s'!",
                    port.code_name,
                    str(self),
                )
                return False
        except AttributeError as err:
            LOG.debug("'%s': Got Attribute Error: '%s'", str(self), str(err))
            return False

        LOG.debug("Port '%s' assigned to new interface '%s'.", port.code_name,
                  str(inter))
        return True