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)
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