def _def_to_proto(self) -> edgir.Link: for cls in self._get_bases_of( BaseBlock ): # type: ignore # TODO avoid 'only concrete class' error assert issubclass(cls, Link) pb = self._populate_def_proto_block_base(edgir.Link()) pb = self._populate_def_proto_block_contents(pb) pb = self._populate_def_proto_param_init(pb) # specifically ignore the port initializers # actually generate the links and connects ref_map = self._get_ref_map(edgir.LocalPath()) self._connects.finalize() self._links_order: Dict[str, str] = self.Metadata({}) for name, connect in self._connects.items_ordered(): self._links_order[str(len(self._links_order))] = f"{name}" connect_elts = connect.generate_connections() assert connect_elts is not None and connect_elts.link_type is not None, "bad connect definition in link" link_path = edgir.localpath_concat(edgir.LocalPath(), name) pb.links[ name].lib_elem.target.name = connect_elts.link_type._static_def_name( ) for idx, (self_port, link_port_path) in enumerate( connect_elts.bridged_connects): # TODO handle Vector types if isinstance( self_port, DerivedVector ): # TODO unify once we get rid of ref_map, especially to be more robust pb.constraints[ f"(export){name}_{idx}"].exported.exterior_port.map_extract.container.ref.CopyFrom( ref_map[self_port.base]) pb.constraints[ f"(export){name}_{idx}"].exported.exterior_port.map_extract.path.steps.add( ).name = self_port.base._get_elt_sample()._name_of( self_port.target) else: pb.constraints[ f"(export){name}_{idx}"].exported.exterior_port.ref.CopyFrom( ref_map[self_port]) pb.constraints[ f"(export){name}_{idx}"].exported.internal_block_port.ref.CopyFrom( edgir.localpath_concat(link_path, link_port_path)) self._namespace_order.append(f"(export){name}_{idx}") return pb
def _populate_def_proto_block_contents(self, pb: ProtoType) -> ProtoType: """Populates the contents of a block proto: constraints""" assert self._elaboration_state == BlockElaborationState.post_contents or \ self._elaboration_state == BlockElaborationState.post_generate self._constraints.finalize() ref_map = self._get_ref_map(edgir.LocalPath()) for (name, constraint) in self._constraints.items(): pb.constraints[name].CopyFrom(constraint._expr_to_proto(ref_map)) for (name, port) in self._ports.items(): if port in self._required_ports: if isinstance(port, Port): pb.constraints[f'(reqd){name}'].CopyFrom( port.is_connected()._expr_to_proto(ref_map)) elif isinstance(port, Vector): pb.constraints[f'(reqd){name}'].CopyFrom( (port.length() > 0)._expr_to_proto(ref_map)) else: raise ValueError(f"unknown non-optional port type {port}") self._namespace_order.append(f'(reqd){name}') return pb
def generate_connections(self) -> ConnectedPorts.Connections: link_ref_map = self.link._get_ref_map_allocate(edgir.LocalPath()) bridged_connects = [(port, link_ref_map[tar]) for port, tar in self.bridged_connects] direct_connects = [(port, link_ref_map[tar]) for port, tar in self.direct_connects] return ConnectedPorts.Connections(type(self.link), bridged_connects, direct_connects)
def map_port_allocate(ref: Refable, path: edgir.LocalPath) -> edgir.LocalPath: if isinstance(ref, BaseVector): new_path = edgir.LocalPath() new_path.CopyFrom(path) new_path.steps.append( edgir.LocalStep(reserved_param=edgir.ALLOCATE)) return new_path else: return path
def _parse_param_values( self, values: Iterable[Tuple[edgir.LocalPath, edgir.LitTypes]]) -> None: ref_map = self._get_ref_map(edgir.LocalPath()) reverse_ref_map = { path.SerializeToString(): refable for refable, path in ref_map.items() } self._param_values = IdentityDict() for (path, value) in values: path_expr = reverse_ref_map[path.SerializeToString()] assert isinstance(path_expr, ConstraintExpr) self._param_values[path_expr] = value
def _populate_def_proto_param_init( self, pb: ProtoType, ignore_params: IdentitySet[ConstraintExpr] = IdentitySet() ) -> ProtoType: ref_map = self._get_ref_map(edgir.LocalPath()) # TODO dedup ref_map for (name, param) in self._parameters.items(): if param.initializer is not None and param not in ignore_params: pb.constraints[f'(init){name}'].CopyFrom( AssignBinding.make_assign(param, param.initializer, ref_map)) self._namespace_order.append(f'(init){name}') return pb
def generate_connections(self) -> Optional[ConnectedPorts.Connections]: if self.connect is not None: return self.connect.generate_connections() elif len(self.ports) == 2: # for direct exports exterior_port = [ port for port in self.ports if port._block_parent() is self.parent ] internal_port = [ port for port in self.ports if port._block_parent() is not self.parent ] assert len(exterior_port) == 1 and len(internal_port) == 1 return self.Connections( None, [(exterior_port[0], edgir.LocalPath()) ], # TODO maybe have a separate type for bridged connections? [(internal_port[0], edgir.LocalPath())]) elif len(self.ports) == 1: return None else: raise ValueError("internal error: bad connection state")
def _populate_def_proto_port_init( self, pb: ProtoType, ignore_ports: IdentitySet[BasePort] = IdentitySet() ) -> ProtoType: # TODO this is structurally ugly! # TODO TODO: for non-generated exported initializers, check and assert default-ness ref_map = self._get_ref_map(edgir.LocalPath()) # TODO dedup ref_map def check_recursive_no_initializer(port: BasePort, path: List[str]) -> None: if isinstance(port, (Port, Bundle)): for (name, subparam) in port._parameters.items(): assert subparam.initializer is None, f"unexpected initializer in {port} at {path}" if isinstance(port, Bundle): for (name, subport) in port._ports.items(): check_recursive_no_initializer(subport, path + [name]) elif isinstance(port, Vector): check_recursive_no_initializer(port._get_elt_sample(), path) # TODO needs to be something like sealed types for match comprehensiveness def process_port_inits(port: BasePort, path: List[str]) -> None: if port in ignore_ports: return if isinstance(port, (Port, Bundle)): for (name, subparam) in port._parameters.items(): if subparam.initializer is not None: pb.constraints[ f"(init){'.'.join(path + [name])}"].CopyFrom( AssignBinding.make_assign( subparam, subparam._to_expr_type( subparam.initializer), ref_map)) self._namespace_order.append( f"(init){'.'.join(path + [name])}") if isinstance(port, Bundle): for (name, subport) in port._ports.items(): process_port_inits(subport, path + [name]) elif isinstance(port, Vector): check_recursive_no_initializer(port._get_elt_sample(), path) # TODO needs to be something like sealed types for match comprehensiveness for (name, port) in self._ports.items(): process_port_inits(port, [name]) return pb
def _populate_def_proto_block_base(self, pb: ProtoType) -> ProtoType: """Populates the structural parts of a block proto: parameters, ports, superclasses""" assert self._elaboration_state == BlockElaborationState.post_contents or \ self._elaboration_state == BlockElaborationState.post_generate self._parameters.finalize() self._ports.finalize() if (self.__class__, 'abstract') in self._elt_properties: assert isinstance(pb, edgir.HierarchyBlock) pb.is_abstract = True pb.self_class.target.name = self._get_def_name() for cls in self._get_bases_of( BaseBlock ): # type: ignore # TODO avoid 'only concrete class' error super_pb = pb.superclasses.add() super_pb.target.name = cls._static_def_name() for (name, param) in self._parameters.items(): assert isinstance(param.binding, ParamBinding) pb.params[name].CopyFrom(param._decl_to_proto()) for (name, port) in self._ports.items(): pb.ports[name].CopyFrom(port._instance_to_proto()) self._constraints.finalize() # needed for source locator generation # generate base-block order # TODO unified namespace order # TODO this also appends to end, which may not be desirable for name in chain(self._parameters.keys_ordered(), self._ports.keys_ordered(), self._constraints.keys_ordered()): self._namespace_order.append(name) ref_map = self._get_ref_map(edgir.LocalPath()) pb.meta.CopyFrom(self._metadata_to_proto(self._metadata, [], ref_map)) return pb
def _populate_def_proto_block_generator( self, pb: edgir.HierarchyBlock) -> edgir.HierarchyBlock: assert self._generators, f"{self} did not define any generator functions" ref_map = self._get_ref_map(edgir.LocalPath()) for (name, record) in self._generators.items(): pb.generators[ name] # even if rest of the fields are empty, make sure to create a record # TODO maybe there should be done data here? for req_param in record.req_params: pb.generators[name].required_params.add().CopyFrom( ref_map[req_param]) for req_port in record.req_ports: pb.generators[name].required_ports.add().CopyFrom( ref_map[req_port]) for connect_block in record.connect_blocks: pb.generators[name].connected_blocks.add().CopyFrom( ref_map[connect_block]) return pb
def _get_contained_ref_map(self) -> IdentityDict[Refable, edgir.LocalPath]: return self.elt_sample._get_ref_map(edgir.LocalPath())
def _populate_def_proto_hierarchy( self, pb: edgir.HierarchyBlock) -> edgir.HierarchyBlock: self._blocks.finalize() self._connects.finalize() self._chains.finalize() ref_map = self._get_ref_map(edgir.LocalPath()) # opportunistic check in the frontend that all internal ports marked connected are connected all_connected_ports = IdentitySet[BasePort]() for name, connect in self._connects.items(): if len(connect.ports) > 1: all_connected_ports.update(connect.ports) for name, block in self._blocks.items(): pb.blocks[name].lib_elem.target.name = block._get_def_name() # actually generate the links and connects link_chain_names = IdentityDict[ ConnectedPorts, List[str]]() # prefer chain name where applicable # TODO generate into primary data structures for name, chain in self._chains.items_ordered(): for i, connect in enumerate(chain.links): link_chain_names.setdefault(connect, []).append(f"{name}_{i}") for name, connect in self._connects.items_ordered(): if connect in link_chain_names: if not name.startswith('anon_'): pass # prefer a named link above all else else: name = link_chain_names[connect][ 0] # arbitrarily pick the first one for now, TODO disambiguate? connect_elts = connect.generate_connections() if connect_elts is None: # single port net - effectively discard pass elif connect_elts.link_type is None: # generate direct export pb.constraints[ f"(conn){name}"].exported.exterior_port.ref.CopyFrom( ref_map[connect_elts.bridged_connects[0][0]]) pb.constraints[ f"(conn){name}"].exported.internal_block_port.ref.CopyFrom( ref_map[connect_elts.direct_connects[0][0]]) self._namespace_order.append(f"(conn){name}") else: # generate link link_path = edgir.localpath_concat(edgir.LocalPath(), name) self._namespace_order.append(f"{name}") pb.links[ name].lib_elem.target.name = connect_elts.link_type._static_def_name( ) for idx, (self_port, link_port_path) in enumerate( connect_elts.bridged_connects): assert isinstance(self_port, Port) assert self_port.bridge_type is not None port_name = self._name_of(self_port) pb.blocks[ f"(bridge){port_name}"].lib_elem.target.name = self_port.bridge_type._static_def_name( ) self._namespace_order.append(f"(bridge){port_name}") bridge_path = edgir.localpath_concat( edgir.LocalPath(), f"(bridge){port_name}") pb.constraints[ f"(bridge){name}_b{idx}"].exported.exterior_port.ref.CopyFrom( ref_map[self_port]) pb.constraints[ f"(bridge){name}_b{idx}"].exported.internal_block_port.ref.CopyFrom( edgir.localpath_concat(bridge_path, 'outer_port')) self._namespace_order.append(f"(bridge){name}_b{idx}") pb.constraints[ f"(conn){name}_b{idx}"].connected.block_port.ref.CopyFrom( edgir.localpath_concat(bridge_path, 'inner_link')) pb.constraints[ f"(conn){name}_b{idx}"].connected.link_port.ref.CopyFrom( edgir.localpath_concat(link_path, link_port_path)) self._namespace_order.append(f"(conn){name}_b{idx}") for idx, (subelt_port, link_port_path) in enumerate( connect_elts.direct_connects): pb.constraints[ f"(conn){name}_d{idx}"].connected.block_port.ref.CopyFrom( ref_map[subelt_port]) pb.constraints[ f"(conn){name}_d{idx}"].connected.link_port.ref.CopyFrom( edgir.localpath_concat(link_path, link_port_path)) self._namespace_order.append(f"(conn){name}_d{idx}") # generate block initializers for (block_name, block) in self._blocks.items(): for (block_param_name, block_param) in block._init_params.items(): if block_param.initializer is not None: pb.constraints[ f'(init){block_name}.{block_param_name}'].CopyFrom( # TODO better name AssignBinding.make_assign( block_param, block_param._to_expr_type( block_param.initializer), ref_map)) self._namespace_order.append( f'(init){block_name}.{block_param_name}') # generate H-block-specific order for name in self._blocks.keys_ordered(): self._namespace_order.append(name) return pb