def __check_vector_assignments__(self):
        """! @brief Checks the plausibility of the vector assignments.
        If assignments are overlapping or out of bounds, an AsConnectionError
        is raised."""
        if not self.data_width.is_resolved():
            raise AsConnectionError(
                self,
                ("Cannot partially assign to/from signal '{}' "
                 "with unresolved data width '{}' (generics present)!").format(
                     self, self.data_width),
            )
        bitwidth = self.data_width.get_bit_width()
        for vector_map in (self.vector_map_incoming, self.vector_map_outgoing):
            assignment_map = set()
            # Check plausibility of vector assignment (out of bounds, overlap, etc.)
            for assignment_id in vector_map:
                # Is bit position out of bounds?
                if assignment_id > bitwidth:
                    raise AsConnectionError(
                        self,
                        ("Vector assignment to/from signal '{}' at "
                         "bit index {} is out of bounds for signal type: {}"
                         ).format(
                             self,
                             assignment_id,
                             as_help.get_printable_datatype(self),
                         ),
                    )
                # Get source port/signal
                signal = vector_map[assignment_id]
                # Is its data width resolved?
                if not signal.data_width.is_resolved():
                    raise AsConnectionError(
                        signal,
                        ("Cannot assign {} to/from vector signal {}. "
                         "Data width '{}' is unresolved!").format(
                             signal, self,
                             as_help.get_printable_datatype(signal)),
                    )

                # Get bit width and a tuple with all bit positions to assign to
                signal_bit_width = signal.data_width.get_bit_width()
                new_map = tuple(
                    range(assignment_id, assignment_id + signal_bit_width))
                # Does the new assignment overlap with previous assignments?
                if assignment_map.intersection_update(new_map):
                    raise AsConnectionError(
                        signal,
                        ("Vector assignment to/from signal '{}' from '{}' "
                         "overlaps with previous assignments! List: {}"
                         ).format(self, signal, vector_map),
                    )
Beispiel #2
0
    def __connect_layer_input__(self, inter: Interface, layer: AsLayer,
                                anchor: WindowRef):
        window = isinstance(inter, AsWindowInterface)

        if window:
            if len(inter.references) > 1:
                LOG.error(
                    ("Tried connection interface '%s' input to layer '%s'"
                     " with more than one port!"),
                    inter.name,
                    layer.name,
                )
                raise AsConnectionError(
                    ("Layer input interfaces must have only"
                     " one port!"))
            else:
                # There should only be one port / reference for layer input interfaces
                port = inter.references[0].port
        elif isinstance(inter, Interface):
            try:  # Assuming AsStream interface!
                port = inter.get_port("data")
            except NameError as err:
                LOG.error("Could not find port 'data' in '%s'! - '%s'",
                          inter.name, str(err))
                raise AsConnectionError(
                    ("When connecting '{}' in 2D Window "
                     "Pipeline to '{}'. Missing port 'data'! Might not be "
                     "AsStream!").format(inter.name, layer.name))
        if anchor is None:
            anchor = WindowRef(0, 0)
        module = inter.parent
        # The input of each layer should be @ (0,0)
        # The pixel offset between the base layer and all other layers
        # is determined by each layers 'offset' attribute
        layer.set_input(AsConnectionRef(WindowRef(0, 0), port))
        layer.set_offset(WindowRef(0, 0))

        # Register the layer input connection with the layer, interface
        # and module objects involved
        layer.interfaces.append(inter)
        layer.modules.append(module)
        # Assuming window interface is only present in AsWindowModule
        if window:
            module.output_layers.append(layer)
            inter.layer = layer
        elif isinstance(module, AsModule):
            inter.outgoing.append(layer)
        else:
            LOG.error("Connected to unkown object type!")
            raise TypeError("Unkown module type for connection: {}".format(
                type(module)))
Beispiel #3
0
 def set_input(self, input_ref: AsConnectionRef):
     """Set the data input reference for this layer.
     Each layer's input reference should be @ (0,0), with the layer.offset
     signifying the layer's offset from the base layer."""
     port = input_ref.port
     # Check data widths if the port's data width is already fixed
     if is_data_width_resolved(port.data_width):
         port_data_width = Port.data_width_to_string(port.data_width)
         if self.data_width != port_data_width:
             LOG.error(
                 ("Incompatiple layer input detected! '%s'(%s) -> "
                  "'%s'(%s)"),
                 port.code_name,
                 port_data_width,
                 self.name,
                 self.data_width,
             )
             raise AsConnectionError(
                 "Layer and input port data widths differ!")
     if ((input_ref.row < self.parent.rows)
             and (input_ref.col < self.parent.columns)
             and (input_ref.row >= 0 and input_ref.col >= 0)):
         self.input = input_ref
         LOG.debug("Set input for layer '%s' to %s.", self.name,
                   str(input_ref))
     else:
         LOG.error("Input '%s' is out of bounds!", str(input_ref))
def generate_from_vector_assignment_strings(signal: GenericSignal) -> list:
    """! @brief Generate a string assigning to vector signal 'signal'
    from its vector map dictionary."""
    # Comment and empty line for readability
    out = ["\n  -- Assign from signal {}:".format(signal.code_name)]
    # Iterate over the vector map starting from the lowest bit
    for idx in sorted(signal.vector_map_outgoing.keys()):
        target = signal.vector_map_outgoing[idx]
        sep = target.data_width.sep
        # Get the target signal
        if isinstance(target, GenericSignal):
            pass
        elif isinstance(target, Port):
            if target.glue_signal:
                target = target.glue_signal
            else:
                raise AsConnectionError(
                    target,
                    "Port '{}' does not have a glue signal assigned!".format(
                        target),
                )
        target_bit_width = target.data_width.get_bit_width()
        # Generate the assignment string for this target
        width = ""
        if sep == "to":
            width = "{} to {}".format(idx, idx + target_bit_width - 1)
        elif sep == "downto":
            width = "{} downto {}".format(idx + target_bit_width - 1, idx)
        else:
            width = str(idx)
        out.append("  {} <= {}({});".format(target.code_name, signal.code_name,
                                            width))
    # Leave an empty line after assignment for readability
    out.append("")
    return out
 def connect(self, target):
     """! @brief Connect this Port to another Port, Interface or AsModule."""
     try:
         self.parent.__connect__(self, target)
     except AttributeError:
         raise AsConnectionError(
             self,
             ("GenericSignal '{}' not associated to an AsModuleGroup!"
              " - in '{}'").format(self.code_name, self.parent.name),
         )
 def add_window_interface_row(self,
                              window_in,
                              row_index: int,
                              input_port: Port = None) -> GenericSignal:
     """! @brief Add a window interface row as a buffer target.
     Adds itself as the buffer row into the WindowDef of the window port,
     at row index 'row_index'. Generates an intermediate signal for data
     transport between the output of the buffer component and assigning
     the window signal, which is returned on success.
     @param window_in  The window interface to supply with data
     @param row_index  The row of the window interface to target
     @param input_port  The Port supplying the image data
     @return  intermediate signal of this buffer's delayed data output
     """
     try:
         wport = window_in.window_port
         window = window_in.window
     except AttributeError:
         raise AsConnectionError(
             window_in,
             "Invalid object passed to 'add_window_interface' for "
             "buffer '{}': {}!".format(self.name, window_in),
         )
     source = None
     if row_index == 0:
         if wport.glue_signal:
             source = wport.glue_signal
         elif wport.incoming.glue_signal:
             source = wport.incoming.glue_signal
         else:
             source = wport.incoming
     else:
         source = input_port
     # Register the source with this buffer
     if not self.add_input(
             source,
             is_window_signal=True,
             to_window_port=wport,
             window_row_idx=row_index,
     ):
         return None
     window.rows[row_index] = self
     signame = (window_in.parent.name + "_" + wport.code_name + "_row_" +
                str(row_index))
     im_sig = self.pipe.define_signal(signame,
                                      data_type="std_logic_vector",
                                      data_width=wport.data_width)
     set_delay(im_sig, self.output_delay)
     self.add_output(im_sig)
     return im_sig
Beispiel #7
0
    def __run_user_connect_calls__(self):
        """Run the connect method calls that the user specified for the pipeline
        Starts the recursive job runner."""
        for job in self.user_connections:
            # DEBUG
            print("{} ({}) [{}] <-> {} [{}]".format(
                job.inter.name,
                job.inter.unique_name,
                job.inter.parent.name,
                job.layer.name,
                job.type,
            ))
        # Run the base layers first:
        # (layers where the input comes from outside of the pipeline)
        LOG.debug("Pipeline: Start handling user connections for pipeline...")
        base = self.__get_base_layers__()
        if not base:  # Need at least one base layer...
            LOG.debug("Missing base layer among: '%s'", self.layers)
            raise AsConnectionError(("Could not start connection process, "
                                     "no base layer specified!"))
        LOG.debug("Pipeline: Base layers %s:", str(base))

        # Sort user connection jobs
        input_cons = [
            con for con in self.user_connections
            if con.type is self.LAYER_INPUT
        ]
        output_cons = [
            con for con in self.user_connections
            if con.type is self.LAYER_OUTPUT
        ]
        LOG.debug(
            "Pipeline: Sorted %i input jobs and %i output jobs.",
            len(input_cons),
            len(output_cons),
        )

        done_jobs = []  # Has to stay consistent between job handler calls!
        for layer in base:
            LOG.debug("Pipeline: Starting job handler for layer '%s'...",
                      layer.name)
            self.__rec_conjob_handler__(layer, input_cons, output_cons,
                                        done_jobs)

        # Use the resulting list of connection jobs (order is important!)
        # to calculate the physical pixel references in order
        self.pipeline_traversal = done_jobs
        self.__calc_phys_addr__()
 def make_external(self, external_port_name: str = "") -> bool:
     if self.parent is not None:
         if not external_port_name:
             portname = self.code_name + "_sig_ext"
         else:
             portname = external_port_name
         port_dir = "out"
         module = self.parent
         ext_port = module.get_port(portname, suppress_error=True)
         if ext_port is not None:
             if (ext_port.direction != port_dir
                     or ext_port.incoming is not None):
                 LOG.error(
                     ("Externalizing signal '%s': Port with name '%s' "
                      "already exists in module '%s'! Define another "
                      "port name to resolve this conflict."),
                     self.code_name,
                     portname,
                     module.name,
                 )
                 raise AsConnectionError(
                     self,
                     "Port with name '{}' already exists.".format(portname),
                     "While making signal '{}' external".format(
                         self.code_name),
                 )
         if ext_port is None:
             ext_port = module.define_port(
                 name=portname,
                 code_name=portname,
                 direction=port_dir,
                 data_type=self.data_type,
                 data_width=self.data_width,
             )
         ext_port.incoming = self
         self.outgoing.append(ext_port)
         return True
     LOG.error(
         ("Could not externalize signal '%s': "
          "Signal is not associated to any module!"),
         self.code_name,
     )
     return False
    def add_output(self, data_output: Port, update_delay: bool = True) -> bool:
        """! @brief Add a data output Port to this buffer.
        The next unconnected output index is used for the connection.
        @param data_output  The Port to supply with data from this buffer
        @param update_delay  Whether or not to update the strobe delay of the Port
        @return  True on success, else False
        """
        if data_output in (out.port for out in self.outputs):
            LOG.warning("Port output '%s' already in '%s'!", str(data_output),
                        self.name)
            return False
        if data_output.data_width.get_bit_width() != self.inputs[-1].bit_width:
            LOG.error(
                ("Bit width of data output '%s' added to '%s' does not "
                 "match corresponding data input '%s': %i <-> %i"),
                str(data_output),
                self.name,
                str(self.inputs[-1].port),
                data_output.data_width.get_bit_width(),
                self.inputs[-1].bit_width,
            )
            return False
        new_output = AsPipelineDataInfo(data_output, self.bit_width_out)
        if new_output.bit_width is None:
            raise AsConnectionError(
                data_output,
                ("Port added to buffer '{}' in '{}' "
                 "has unresolved data width [{}]!").format(
                     self.name, self.pipe.name, data_output.data_width),
            )
        self.outputs.append(new_output)

        if update_delay:
            new_output.delay = self.output_delay
            set_delay(new_output.port, self.output_delay)
        self.bit_width_out += new_output.bit_width
Beispiel #10
0
    def _instantiate_module(self, module: AsModule) -> str:
        """! @brief Generate VHDL code as a list of strings to instantiate 'module'.
        Handles generic assignment and port mapping."""

        # -> Generating the generic map <-
        gen_str = []
        if module.generics:
            gen_str.append("  generic map(")
            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 = tgen.get_value()
                    # Skip generics that use the default value
                    if ret == tgen.default_value:
                        continue
                gen_str.append("    {} => {},".format(tgen.code_name, ret))
        if len(gen_str) > 1:
            # Remove the last comma "," and add a closing bracket
            gen_str[-1] = gen_str[-1].strip(",")
            gen_str.append("  )\n")
            gen_str = "\n".join(gen_str)
        else:
            gen_str = ""

        # -> Generating the port map <-
        port_str = ["  port map("]
        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 = "    {} => {},"
            else:
                templ_str = "    {} => {}\n  );"

            # -> Has glue signal <-
            # Port mapping target is the port's glue signal
            if port.glue_signal:
                glue = port.glue_signal
                try:
                    target = glue if isinstance(glue, str) else glue.code_name
                except AttributeError:
                    raise AsConnectionError(
                        port,
                        "Unsuitable object assigned as glue signal of port! "
                        "'{}: {}'".format(type(glue), str(glue)),
                    )
                # If this glue signal should be included in the signal list
                if isinstance(glue, GlueSignal) and glue.is_signal:
                    # Add the glue signal to the signal list
                    # Assemble vhdl signal declaration string
                    glue_signal_str = "  signal {} : {};".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:  # -> 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:  # -> No 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,
                            get_parent_module(port).name,
                            target,
                        )
                    else:
                        LOG.info(
                            ("Port '%s' of module '%s' was left "
                             "unconnected, automatically set to [%s]!"),
                            port.code_name,
                            get_parent_module(port).name,
                            target,
                        )
            # Insert the values in the format string and add to the return list
            port_str.append(templ_str.format(port.code_name, target))
        port_str = "\n".join(port_str)

        # --> OUT
        entity_keyword_str = ("" if isinstance(module, AsModuleWrapper) else
                              " entity")
        out_str = as_static.MODULE_INSTANTIATION_TEMPLATE.format(
            module_name=module.name,
            entity_name=module.entity_name,
            entity_keyword=entity_keyword_str,
            port_map=port_str,
            generic_map=gen_str,
        )
        return out_str
    def add_input(
        self,
        data_input: Port,
        is_window_signal: bool = False,
        to_window_port=None,
        window_row_idx: int = 0,
        check_delay: bool = True,
    ) -> bool:
        """! @brief Add a Port as an input for this pipeline buffer row.
        Extends this buffer by the port.
        @param data_input  The Port to add as an input of this buffer
        @param is_window_signal  Marks this part of the data buffer as a window signal
        @param to_window_port  The window port targeted by this part of the buffer
                               Must be set if is_window_signal is set to True
        @param window_row_idx  The row index this part of the buffer targets
                               Must be set if is_window_signal is set to True
        @param check_delay  Whether or not to make sure the input delay of the
                            Port added matches the other input Ports
        @return  True on success, else False
        """
        # Do we already have this data input?
        if data_input in (inp.port for inp in self.inputs):
            LOG.warning("Port input '%s' already in '%s'!", str(data_input),
                        self.name)
            return False
        # Does the data input have a delay value?
        if get_delay(data_input) is None:
            LOG.error(
                "Port '%s' added to '%s' without a data delay value!",
                str(data_input),
                self.name,
            )
            return False
        # If this is the first input, use it's delay as the input delay
        if self.input_delay is None:
            self.input_delay = get_delay(data_input)
            self.output_delay = self.input_delay + self.length
        elif check_delay and get_delay(data_input) != self.input_delay:
            LOG.error(
                "Port '%s' added to '%s' with differing input delays!",
                str(data_input),
                self.name,
            )
            return False
        # Create and append input management object
        new_input = AsPipelineDataInfo(data_input, self.bit_width_in)
        if new_input.bit_width is None:
            raise AsConnectionError(
                data_input,
                ("Port added to buffer '{}' in '{}' "
                 "has unresolved data width [{}]!").format(
                     self.name, self.pipe.name, data_input.data_width),
            )
        self.inputs.append(new_input)
        self.is_window_signal.append(is_window_signal)
        if isinstance(to_window_port, list):
            self.to_window_ports.append(to_window_port)
        else:
            self.to_window_ports.append([(to_window_port, window_row_idx)])

        # Update bit width and module generic
        self.bit_width_in += new_input.bit_width
        self.module.set_generic_value("DATA_WIDTH", self.bit_width_in)
        return True
    def __vector_assignment__(self, other, from_bit_index: int,
                              direction: str):
        if "vector" not in self.data_type:
            raise AsConnectionError(
                self,
                ("Cannot partially assign to/from signal '{}'"
                 " with data type '{}'.").format(self, self.data_type),
            )
        # Removing an assignment
        if other is None:
            if direction == "in":
                vector_map = self.vector_map_incoming
                con_list = self.incoming
            else:
                vector_map = self.vector_map_outgoing
                con_list = self.outgoing
            # Get the connecting signal
            try:
                other = self.vector_map_incoming[from_bit_index]
            except KeyError:
                return False
            # Remove the references in the vector map and connection list
            con_list.remove(other)
            vector_map.pop(from_bit_index)
            return True

        if other.data_type not in (
                "std_logic",
                "std_logic_vector",
                "bit",
                "bit_vector",
                "std_ulogic",
                "std_ulogic_vector",
        ):
            raise AsConnectionError(
                self,
                ("Cannot partially assign {} with data type '{}' "
                 "to/from signal '{}'.").format(other, other.data_type, self),
            )
        omod = get_parent_module(other)
        if (not isinstance(other, GenericSignal)) and (
                omod.modlevel > get_parent_module(self).modlevel):
            if other.glue_signal:
                other = other.glue_signal
            else:
                signame = omod.name + "_" + other.code_name + "_signal"
                sig = omod.parent.get_signal(signame)
                if sig is None:
                    sig = omod.parent.define_signal(signame, other.data_type,
                                                    other.data_width)
                sig.generics = copy.copy(other.generics)
                omod.chain.__connect__(sig, other, top=omod.parent)
                other = sig

        # Make the connection
        if direction == "in":
            other.outgoing.append(self)
            self.incoming.append(other)
            self.vector_map_incoming[from_bit_index] = other
            # Add a glue signal if not present
            if not isinstance(other, GenericSignal):
                if other.glue_signal is None:
                    other.glue_signal = GlueSignal.generate_glue_signal(
                        other, other, self)
        else:
            other.incoming.append(self)
            self.outgoing.append(other)
            self.vector_map_outgoing[from_bit_index] = other
Beispiel #13
0
    def __connect_layer_output__(self, inter: Interface, layer: AsLayer,
                                 anchor: WindowRef):
        # TODO: Handle anchor case
        window = isinstance(inter, AsWindowInterface)

        # Check if the interface is ready to be connected
        if window:
            if not inter.update_footprint():
                LOG.error(
                    ("Could not connect interface '%s'! The generics used in "
                     "'%s' need to be defined before making the connection!"),
                    inter.name,
                    inter.data_type,
                )
                raise ValueError(
                    "Undefined generics in interface '{}': '{}'".format(
                        inter.name, inter.data_type))
        if not anchor:
            anchor = WindowRef(0, 0)
        if layer.input is None:  # Just making sure!
            LOG.error(
                ("Attempted to connect output interface '%s' to layer"
                 " '%s' without an input!"),
                inter.name,
                layer.name,
            )
            raise AsConnectionError(("Cannot connect outputs to layer "
                                     "without an input!"))

        module = inter.parent

        if window:
            if module.user_phys_offset:
                module.offset = module.user_phys_offset
            else:
                module.offset = anchor
            # For each input port of the interface
            for pref in inter.references:
                layer_ref = layer.add_output(pref)
                # Add the resulting reference to the module
                module.input_refs.append(layer_ref)
                # TODO: Testing!
        else:
            try:
                port = inter.get_port("data", suppress_error=True)
            except NameError as err:
                LOG.error("Could not find port 'data' in '%s'! - '%s'",
                          inter.name, str(err))
                raise AsConnectionError(
                    ("When connecting '{}' in 2D Window "
                     "Pipeline to '{}'. Missing port 'data'! Might not be "
                     "AsStream!").format(inter.name, layer.name))
            pref = AsConnectionRef(WindowRef(0, 0), port)
            layer.add_output(pref, True)

        # Register this connection with the module, layer and interface involved
        layer.interfaces.append(inter)
        layer.modules.append(module)
        if window:
            module.input_layers.append(layer)
            inter.layer = layer
        elif isinstance(module, AsModule):
            inter.incoming.append(layer)