def _elaborate(self): """Verify all the ``clock`` and ``combinational_sources`` attributes are valid.""" for port in itervalues(self.ports): if port.is_clock: continue if port.clock is not None: clock = self.ports.get(port.clock, None) if clock is None: raise PRGAInternalError( "Clock '{}' of port '{}' not found in primitive '{}'". format(port.clock, port, self)) elif not clock.is_clock: raise PRGAInternalError( "Clock '{}' of port '{}' in primitive '{}' is not a clock" .format(clock, port, self)) if port.direction.is_input: continue for source_name in port.combinational_sources: source = self.ports.get(source_name, None) if source is None: raise PRGAInternalError( "Combinational source '{}' of port '{}' not found in primitive '{}'" .format(source_name, port, self)) elif not source.direction.is_input: raise PRGAInternalError( "Combinational source '{}' of port '{}' in primitive '{}' is not an input" .format(source_name, port, self))
def segment_fc(self, segment, all_sections=False): """Get the FC value for a specific segment. Args: segment (`Segment`): all_sections (:obj:`bool`): if all sections of a segment longer than 1 should be taken into consideration Returns: :obj:`int`: the calculated FC value """ multiplier = segment.length if all_sections else 1 fc = self.overrides.get(segment.name, self.default) if isinstance(fc, int): if fc < 0 or fc >= segment.width: raise PRGAInternalError( "Invalid FC value ({}) for segment '{}'".format( fc, segment.name)) return fc * multiplier elif isinstance(fc, float): if fc < 0 or fc > 1: raise PRGAInternalError( "Invalid FC value ({}) for segment '{}'".format( fc, segment.name)) return int(ceil(fc * segment.width * multiplier)) else: raise PRGAInternalError( "Invalid FC value ({}) for segment '{}'".format( fc, segment.name))
def hierarchical_source(net, inplace=False): """Get the direct driver of hierarchical bit, ``net``.""" instance, bit = net if bit.is_bus: raise PRGAInternalError("'{}' is a bus".format(net)) elif bit.net_type.is_pin: raise PRGAInternalError("'{}' is a pin".format(net)) if bit.direction.is_output: if bit.parent.is_leaf_module or bit.logical_source is UNCONNECTED: # we don't search combinational paths through a leaf module here. # do that in the caller's context return None else: return hierarchical_net(bit.logical_source, instance, inplace) elif len(instance) == 0: return None else: if inplace: leaf = instance.pop() else: instance, leaf = instance[:-1], instance[-1] source_bit = leaf.logical_pins[bit.bus.key][bit.index].logical_source if source_bit is UNCONNECTED: return None else: return hierarchical_net(source_bit, instance, inplace)
def _add_port(self, port): """Add a port to this module. Args: port (`AbstractPort`): Returns: `AbstractPort`: Echoing back the added port """ if port.in_physical_domain and not self.in_physical_domain: raise PRGAInternalError( "Cannot add a physical port '{}' to a non-physical module '{}'" .format(port, self)) elif port.in_logical_domain and not self.in_logical_domain: raise PRGAInternalError( "Cannot add a logical port '{}' to a non-logical module '{}'". format(port, self)) elif port.parent is not self: raise PRGAInternalError( "Module '{}' is not the parent module of port '{}'".format( self, port)) elif port.key in self._ports: raise PRGAInternalError( "Key '{}' for port '{}' already exists in module '{}'".format( port.key, port, self)) try: return self._ports.setdefault(port.key, port) except AttributeError: raise PRGAInternalError( "Cannot add port '{}' to module '{}'. Port mapping is read-only" .format(port, self))
def _add_instance(self, instance): """Add an instance to this module. Args: instance (`AbstractInstance`): Returns: `AbstractInstance`: Echoing back the added instance """ if instance.in_physical_domain and not self.in_physical_domain: raise PRGAInternalError( "Cannot add a physical instance '{}' to a non-physical module '{}'" .format(instance, self)) elif instance.in_logical_domain and not self.in_logical_domain: raise PRGAInternalError( "Cannot add a logical instance '{}' to a non-logical module '{}'" .format(instance, self)) elif instance.parent is not self: raise PRGAInternalError( "Module '{}' is not the parent module of instance '{}'".format( self, instance)) elif instance.key in self._instances: raise PRGAInternalError( "Key '{}' for instance '{}' already exists in module '{}'". format(instance.key, instance, self)) try: return self._instances.setdefault(instance.key, instance) except AttributeError: raise PRGAInternalError( "Cannot add instance '{}' to module '{}'. Instance mapping is read-only" .format(instance, self))
def physical_cp(self, cp): # 0. shortcut if no changes are to be made if cp is self.physical_cp: return # 1. validate self and parameter if self.in_physical_domain: raise PRGAInternalError( "'{}' is in the physical domain so no counterpart should be set" .format(self)) if cp is not None and not (cp.in_physical_domain and cp.is_sink is self.is_sink): raise PRGAInternalError( "'{}' is not a valid physical counterpart for '{}'".format( cp, self)) # 2. ungroup grouped physical counterpart if there are try: grouped = self.bus._physical_cp for i in range(self.bus.width): l = self.bus._get_or_create_bit(i, False) p = grouped._get_or_create_bit(i, False) l._physical_cp = p del self.bus._physical_cp except AttributeError: pass # 3. update if cp is None: try: del self._physical_cp except AttributeError: pass else: self._physical_cp = cp._get_or_create_static_cp()
def physical_source(self, source): # 0. shortcut if no changes are to be made try: if source is self._physical_source: return except AttributeError: pass # 1. physical source is only valid if this is a physical net if not self.in_physical_domain: raise PRGAInternalError("'{}' is not in the physical domain" .format(self)) # 2. if ``source`` is a bus or a bit: try: if source.is_bus: # 2.1 validate if not (source.in_physical_domain and not source.is_sink and source.width == self.width): raise PRGAInternalError("'{}' is not a {}-bit physical source bus" .format(source, self.width)) # 2.2 clean up ungrouped physical sources try: for bit in self._bits: try: del bit._physical_source except AttributeError: pass except AttributeError: pass # 2.3 update self._physical_source = source elif self.width == 1: # 2.4 validate if not (source.in_physical_domain and not source.is_sink): raise PRGAInternalError("'{}' is not a physical source" .format(source)) # 2.5 clean up grouped physical source try: del self._physical_source except AttributeError: pass # 2.6 update self._get_or_create_bit(0, False)._physical_source = source # 3. if ``source`` is a sequence of bits except AttributeError: # 3.1 validate if not len(source) == self.width: raise PRGAInternalError("Got {} bits but {} expected" .format(len(source), self.width)) # 3.2 clean up grouped physical source try: del self._physical_source except AttributeError: pass # 3.3 create links for i, src in enumerate(source): # 3.3.1 validate if not (src.in_physical_domain and not src.is_sink): raise PRGAInternalError("'{}' is not a physical source" .format(src)) # 3.3.2 update self._get_or_create_bit(i, False)._physical_source = src
def add_user_sources(self, sources): if not self.in_user_domain: raise PRGAInternalError("'{}' is not a user sink".format(self)) for s in sources: s = s._get_or_create_static_cp() if s.is_sink: # we allow non-user sources to be added to this list # this is only used for connection box - switch box bridges # in other cases, checks are done by caller of this function raise PRGAInternalError("'{}' is not a source".format(s)) try: if s not in self._user_sources: self._user_sources.append(s) except AttributeError: self._user_sources = [s]
def _validate_model(self, model): """Validate if ``model`` can be instantiate in this module.""" if model.module_class not in (ModuleClass.primitive, ModuleClass.cluster): raise PRGAInternalError( "Only primitives or clusters may be instantiated in a custer/block." )
def register_blackbox_template(self, name, lib_template=None, techmap_template=None, parameters=None, premap_commands=None): """Register a blackbox template. Args: name (:obj:`str`): Name of the module which activates this entry lib_template (:obj:`str`): Template for generating a blackbox library file techmap_template (:obj:`str`): Template for generating a techmap file parameters (:obj:`Mapping` [:obj:`str`, Any ]): Additional parameters for the template premap_commands (:obj:`Sequence` [:obj:`str` ]): Pre-techmap commands in the Yosys script Blackbox library is auto-generated for custom primitives, so register blackbox template only if you wish to override the blackbox generated by default, or you need to provide techmap file for them. """ if name in self._blackbox_entries: raise PRGAInternalError( "Blackbox template entry '{}' already registered".format(name)) return self._blackbox_entries.setdefault( name, YosysTemplateEntry(lib_template, techmap_template, parameters, premap_commands))
def register_memory_template(self, name, lib_template=None, techmap_template=None, rule_template=None, parameters=None, premap_commands=None): """Register a BRAM rule template. Args: name (:obj:`str`): Name of the module which activates this entry lib_template (:obj:`str`): Template for generating a blackbox library file techmap_template (:obj:`str`): Template for generating a techmap file rule_template (:obj:`str`): Template for generating a BRAM rule parameters (:obj:`Mapping` [:obj:`str`, Any ]): Additional parameters for the template premap_commands (:obj:`Sequence` [:obj:`str` ]): Pre-techmap commands in the Yosys script """ if name in self._memory_entries: raise PRGAInternalError( "Memory rule template entry '{}' already registered".format( name)) return self._memory_entries.setdefault( name, YosysMemoryTemplateEntry(lib_template, techmap_template, rule_template, parameters, premap_commands))
def user_sources(self): if not self.in_user_domain: raise PRGAInternalError("'{}' is not a user sink".format(self)) try: return ReadonlySequenceProxy(self._user_sources) except AttributeError: return tuple()
def _connect(self, source, sink): if not (source.parent is self and not source.is_sink and source.net_class.is_node and (source.in_user_domain or (source.bus.node.node_type.is_segment_bridge and (source.bus.node.bridge_type.is_sboxin_cboxout or source.bus.node.bridge_type.is_sboxin_cboxout2)))): raise PRGAInternalError( "'{}' is not a user-accessible sink node in routing box '{}'". format(source, self)) if not (sink.parent is self and sink.is_sink and sink.net_class.is_node and sink.in_user_domain): raise PRGAInternalError( "'{}' is not a user-accessible source node in routing box '{}'" .format(sink, self)) sink.add_user_sources((source, ))
def remove_user_sources(self, sources=None): # 0. user sources are only valid if this is a user-accessible net if not self.in_user_domain: raise PRGAInternalError( "'{}' is not a user-accessible sink".format(self)) # 1. static bits are always created when user sources are involved return self._get_or_create_static_cp().remove_user_sources(sources)
def remove_user_sources(self, sources=None): if not self.in_user_domain: raise PRGAInternalError("'{}' is not a user sink".format(self)) if sources is None: try: del self._user_sources except AttributeError: pass else: for s in sources: s = s._get_or_create_static_cp() try: self._user_sources.remove(s) except ValueError: raise PRGAInternalError( "'{}' is not a user source of user sink '{}'".format( s, self))
def __init__(self, name, width=1, is_clock=False): if is_clock and width != 1: raise PRGAInternalError("Clock wire must be 1-bit wide") self._name = name self._width = width self._is_clock = is_clock self._bound_to_position = None self._bound_to_subblock = None
def get_switch_path(context, source, sink, drop_cache = False): """Get the switch path from ``source`` to ``sink``. Args: context (`ArchitectureContext`): source (`AbstractSourceBit`): sink (`AbstractSinkBit`): drop_cache (:obj:`bool`): Returns: :obj:`Sequence` [`AbstractSinkBit` ]: A sequence of switch input bits """ if sink.logical_source is source: return tuple() module = source.parent if source.net_type.is_port else source.parent.parent if not drop_cache: cache = context._cache.get('util.switch_path', {}).get(module.name, {}).get( _in_cache_bit_id(sink), None) if cache is not None: path = cache.get(_in_cache_bit_id(source)) if path is None: raise PRGAInternalError("No path from '{}' to '{}' in module '{}'" .format(source, sink, module)) else: return path # map all sources of sink sink_id = _in_cache_bit_id(sink) stack = [(sink, tuple())] cache = context._cache.setdefault('util.switch_path', {}).setdefault(module.name, {}).setdefault( _in_cache_bit_id(sink), {}) while stack: cur_sink, cur_path = stack.pop() cur_source = cur_sink.logical_source if cur_source.net_type.is_const: # UNCONNECTED or CONSTANT connection continue if cur_source.net_type.is_pin and cur_source.net_class.is_switch: # switch output for next_sink in cur_source.parent.switch_inputs: stack.append( (next_sink, cur_path + (next_sink, )) ) else: # other stuff cache[_in_cache_bit_id(cur_source)] = cur_path try: return cache[_in_cache_bit_id(source)] except KeyError: raise PRGAInternalError("No path from '{}' to '{}' in module '{}'" .format(source, sink, module))
def _validate_node(self, node, direction=None): if node.node_type.is_blockport_bridge: if direction is None: raise PRGAInternalError( "Unable to determine port direction for node '{}' in array element '{}'" .format(node, self)) return direction elif node.node_type.is_segment_bridge: if node.bridge_type in (SegmentBridgeType.array_regular, SegmentBridgeType.array_cboxout, SegmentBridgeType.array_cboxout2): if direction is None: raise PRGAInternalError( "Unable to determine port direction for node '{}' in array element '{}'" .format(node, self)) return direction raise PRGAInternalError( "Invalid node '{}' in array element '{}'".format(node, self))
def _validate_node(self, node, direction=None): if node.node_type.is_segment_driver: if direction is PortDirection.input_: raise PRGAInternalError( "Invalid port direction '{}' for node '{}' in switch box '{}'" .format(direction.name, node, self)) return PortDirection.output elif node.node_type.is_segment_bridge: if node.bridge_type in (SegmentBridgeType.sboxin_regular, SegmentBridgeType.sboxin_cboxout, SegmentBridgeType.sboxin_cboxout2): if direction is PortDirection.output: raise PRGAInternalError( "Invalid port direction '{}' for node '{}' in switch box '{}'" .format(direction.name, node, self)) return PortDirection.input_ raise PRGAInternalError("Invalid node '{}' in switch box '{}'".format( node, self))
def perpendicular(self): """`Dimension`: The perpendicular dimension of this dimension.""" if self is Dimension.x: return Dimension.y elif self is Dimension.y: return Dimension.x else: raise PRGAInternalError( "{} does not have a perpendicular Dimension".format(self))
def direction(self): """:obj:`Direction`: The direction of this orientation.""" if self in (Orientation.north, Orientation.east): return Direction.inc elif self in (Orientation.south, Orientation.west): return Direction.dec else: raise PRGAInternalError( "{} does not have a corresponding direction".format(self))
def _connect(self, source, sink, pack_pattern=None): if ((source.net_type.is_port and source.parent is not self) or (source.net_type.is_pin and source.parent.parent is not self) or source.is_sink or not source.in_user_domain): raise PRGAInternalError( "'{}' is not a source in the user domain in module '{}'". format(source, self)) if ((sink.net_type.is_port and sink.parent is not self) or (sink.net_type.is_pin and sink.parent.parent is not self) or not sink.is_sink or not sink.in_user_domain): raise PRGAInternalError( "'{}' is not a sink in the user domain in module '{}'".format( sink, self)) sink.add_user_sources((source, )) if pack_pattern is not None: self._pack_patterns.setdefault( (self.__net_id(source), self.__net_id(sink)), set()).add(pack_pattern)
def opposite(self): """`Direction`: The opposite direction.""" if self is Direction.inc: return Direction.dec elif self is Direction.dec: return Direction.inc else: raise PRGAInternalError( "{} does not have an opposite Direction".format(self))
def instantiate_element(self, element, position): """Instantiate tile or array ``element`` and anchor its \(0, 0\) position to ``position`` in this array. Args: element (`AbstractArrayElement`): position (:obj:`tuple` [:obj:`int`, :obj:`int` ]): """ position = Position(*position) # 1. check if the placement fits in the array or conflicts with any existing placements for x, y in product(range(-1, element.width), range(-1, element.height)): pos_in_elem = Position(x, y) pos_in_array = position + pos_in_elem for dim in iter(Dimension): if element.covers_channel(pos_in_elem, dim): if (not self.covers_channel(pos_in_array, dim) or (element.runs_channel(pos_in_elem, dim) and not self.runs_channel(pos_in_array, dim))): raise PRGAInternalError("Array element '{}' does not fit in array '{}' at {} (channel {})" .format(element, self, pos_in_array, dim.name)) if element.covers_tile(pos_in_elem): if not self.covers_tile(pos_in_array): raise PRGAInternalError("Array element '{}' does not fit in array '{}' at {} (tile)" .format(element, self, position)) elif self.get_root_element(pos_in_array) is not None: raise PRGAInternalError("Conflicting tile at {} when instantiating array element '{}' at {}" .format(pos_in_array, element, position)) if element.covers_sbox(pos_in_elem): if not self.covers_sbox(pos_in_array): raise PRGAInternalError("Array element '{}' does not fit in array '{}' at {} (switch box)" .format(element, self, position)) elif (self.sbox_instances.get(pos_in_array, None) is not None or self.get_root_element_for_sbox(pos_in_array) is not None): raise PRGAInternalError("Conflicting switch box at {} when instantiating array element '{}' at {}" .format(pos_in_array, element, position)) # 2. instantiate and add placeholders instance = ArrayElementInstance(self, element, position) self._element_grid[position.x][position.y] = instance for x, y in product(range(-1, element.width), range(-1, element.height)): pos_in_elem = Position(x, y) pos_in_array = position + pos_in_elem if element.covers_tile(pos_in_elem) and (x != 0 or y != 0): self._element_grid[pos_in_array.x][pos_in_array.y] = pos_in_elem if element.covers_sbox(pos_in_elem): self._sbox_grid[pos_in_array.x][pos_in_array.y] = pos_in_elem return instance
def to_bridge_id(self, position=None, section=None, bridge_type=None): """`SegmentBridgeID`: Convert to another segment bridge ID.""" if self.node_type.is_segment_driver and bridge_type is None: raise PRGAInternalError( "'bridge_type' required when converting segment driver to segment bridge ID" ) return SegmentBridgeID( uno(position, self.position), self.prototype, self.orientation, uno(section, self.section), uno(bridge_type, getattr(self, "bridge_type", None)))
def case(self, *args, **kwargs): """Use this enum as a variable in a switch-case clause.""" try: return args[self.value] except IndexError: try: return kwargs[self.name] except KeyError: raise PRGAInternalError( "Value unspecified for case {}".format(self))
def auto_connect(self, instance, skip_pins=None, quiet=False): """Auto connect all pins of ``instance`` to ports with the same key of this module. Args: instance (`AbstractInstance`): skip_pins (:obj:`Container`): A set of pin keys which should be skipped quiet (:obj:`bool`): If set, skip if no matching port is found for a pin. Otherwise, error is raised """ skip_pins = uno(skip_pins, set()) if instance.parent is not self: raise PRGAInternalError( "Module '{}' is not the parent module of instance '{}'".format( self, instance)) for key, pin in iteritems(instance.pins): if key in skip_pins: continue port = self.ports.get(key) if port is None: if not quiet: raise PRGAInternalError( "No matching port is found in module '{}' for pin '{}'" .format(self, pin)) continue elif port.direction is not pin.direction: if not quiet: raise PRGAInternalError(( "Match found for pin '{}' in module '{}', " "but the matched port '{}' is an {} (the pin is an {})" ).format(pin, self, port, port.direction.case('input', 'output'), pin.direction.case('input', 'output'))) continue elif port.width != pin.width: if not quiet: raise PRGAInternalError(( "Match found for pin '{}' in module '{}', " "but the matched port '{}' is {}-bit wide (the pin is {}-bit wide)" ).format(pin, self, port, port.width, pin.width)) continue elif port.direction.is_input: self.connect(port, pin) else: self.connect(pin, port)
def _add_mode(self, mode): """Add a mode to this primitive. Args: mode (`Mode`): """ if mode.name in self._modes: raise PRGAInternalError( "Mode '{}' already exist in multi-mode primitive '{}'".format( mode.name, self)) return self._modes.setdefault(mode.name, mode)
def instantiate_sbox(self, box, position): """Instantiate switch box ``box``at ``position`` in this array. Args: box (`SwitchBox`): position (:obj:`tuple` [:obj:`int`, :obj:`int` ]): """ position = Position(*position) # 1. check if the placement fits in the array or conflicts with any existing placements if not self.covers_sbox(position): raise PRGAInternalError("Switch box '{}' does not fit in array '{}' at {}" .format(box, self, position)) elif position in self.sbox_instances: raise PRGAInternalError("Conflicting switch box at {} when instantiating switch box '{}' in array {}" .format(position, box, self)) elif self.get_root_element_for_sbox(position) is not None: raise PRGAInternalError("Conflicting tile at {} when instantiating switch box '{}' in array {}" .format(position, box, self)) # 2. instantiate instance = SwitchBoxInstance(self, box, position) self._sbox_grid[position.x][position.y] = instance return instance
def _validate_node(self, node, direction=None): if node.node_type.is_blockport_bridge: if direction is node.prototype.direction: raise PRGAInternalError( "Invalid port direction '{}' for node '{}' in connection box '{}'" .format(direction.name, node, self)) return node.prototype.direction.opposite elif node.node_type.is_segment_bridge: if node.bridge_type.is_cboxin: if direction is PortDirection.output: raise PRGAInternalError( "Invalid port direction '{}' for node '{}' in connection box '{}'" .format(direction.name, node, self)) return PortDirection.input_ elif node.bridge_type.is_cboxout: if direction is PortDirection.input_: raise PRGAInternalError( "Invalid port direction '{}' for node '{}' in connection box '{}'" .format(direction.name, node, self)) return PortDirection.output raise PRGAInternalError( "Invalid node '{}' in connection box '{}'".format(node, self))