def resistance_sheet( width: float = 10, length: float = 5.0, layers: Layers = (LAYER.SLAB90, LAYER.NPP), layer_offsets: Floats = (0, 0.2), pad: ComponentFactory = pad_contact_slab_npp, ) -> Component: """Sheet resistance. Ensures connectivity is kept for pads and the first layer in layers Args: width: length: layers: for the middle part layer_offsets: pad: function to create a pad """ c = Component() pad = pad() pad1 = c << pad pad2 = c << pad r0 = c << compass( size=(length + layer_offsets[0], width + layer_offsets[0]), layer=layers[0]) for layer, offset in zip(layers[1:], layer_offsets[1:]): c << compass(size=(length + offset, width + offset), layer=layer) pad1.connect("e3", r0.ports["e1"]) pad2.connect("e1", r0.ports["e3"]) return c
def pad( size: Tuple[float, float] = (100.0, 100.0), layer: Layer = LAYER.M3, layers_cladding: Optional[Tuple[Layer, ...]] = None, cladding_offsets: Optional[Tuple[float, ...]] = None, ) -> Component: """Rectangular pad with 4 ports (1, 2, 3, 4) Args: width: pad width height: pad height layer: pad layer layers_cladding: cladding_offsets: """ c = Component() rect = compass(size=size, layer=layer) c_ref = c.add_ref(rect) c.add_ports(c_ref.ports) c.info.size = (float(size[0]), float(size[1])) c.info.layer = layer if layers_cladding and cladding_offsets: for layer, cladding_offset in zip(layers_cladding, cladding_offsets): c.add_ref( compass( size=(size[0] + 2 * cladding_offset, size[1] + 2 * cladding_offset), layer=layer, )) return c
def contact( size: Tuple[float, float] = (11.0, 11.0), layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2, LAYER.M3), vias: Optional[Tuple[Optional[ComponentOrFactory], ...]] = (via1, via2), layer_port: Optional[Layer] = None, ) -> Component: """Rectangular contact Args: size: of the layers layers: layers on which to draw rectangles vias: vias to use to fill the rectangles layer_port: if None asumes port is on the last layer """ width, height = size a = width / 2 b = height / 2 layer_port = layer_port or layers[-1] c = Component() c.height = height c.info.size = (float(size[0]), float(size[1])) c.info.layer = layer_port for layer in layers: ref = c << compass(size=(width, height), layer=layer) if layer == layer_port: c.add_ports(ref.ports) vias = vias or [] for via in vias: if via is not None: via = via() if callable(via) else via w, h = via.info["size"] g = via.info["enclosure"] pitch_x, pitch_y = via.info["spacing"] nb_vias_x = (width - w - 2 * g) / pitch_x + 1 nb_vias_y = (height - h - 2 * g) / pitch_y + 1 nb_vias_x = int(floor(nb_vias_x)) or 1 nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array( via, columns=nb_vias_x, rows=nb_vias_y, spacing=(pitch_x, pitch_y) ) cw = (width - (nb_vias_x - 1) * pitch_x - w) / 2 ch = (height - (nb_vias_y - 1) * pitch_y - h) / 2 x0 = -a + cw + w / 2 y0 = -b + ch + h / 2 ref.move((x0, y0)) return c
def resistance_sheet( width: float = 10, layers: Layers = (LAYER.SLAB90, LAYER.NPP), layer_offsets: Floats = (0, 0.2), pad: ComponentFactory = pad_contact_slab_npp, pad_pitch: float = 100.0, ohms_per_square: Optional[float] = None, ) -> Component: """Sheet resistance. keeps connectivity for pads and first layer in layers Args: width: layers: for the middle part layer_offsets: from edge, positive: over, negative: inclusion pad: function to create a pad pad_pitch: ohms_per_square: optional sheet resistance to compute info.resistance """ c = Component() pad = pad() length = pad_pitch - pad.info_child.size[0] pad1 = c << pad pad2 = c << pad r0 = c << compass( size=(length + layer_offsets[0], width + layer_offsets[0]), layer=layers[0] ) for layer, offset in zip(layers[1:], layer_offsets[1:]): c << compass(size=(length + 2 * offset, width + 2 * offset), layer=layer) pad1.connect("e3", r0.ports["e1"]) pad2.connect("e1", r0.ports["e3"]) c.info_child.resistance = ( ohms_per_square * width * length if ohms_per_square else None ) c.add_port("pad1", port_type="vertical_dc", midpoint=pad1.center) c.add_port("pad2", port_type="vertical_dc", midpoint=pad2.center) return c
def _via_iterable( via_spacing: float, wire_width: float, layer1: Tuple[int, int], layer2: Tuple[int, int], via_layer: Tuple[int, int], via_width: float, ) -> Component: """Via""" c = gf.Component() wire1 = c.add_ref(compass(size=(via_spacing, wire_width), layer=layer1)) wire2 = c.add_ref(compass(size=(via_spacing, wire_width), layer=layer2)) viac = c.add_ref(compass(size=(via_width, via_width), layer=via_layer)) via1 = c.add_ref(compass(size=(via_width, via_width), layer=via_layer)) wire1.connect(port="e3", destination=wire2.ports["e1"], overlap=wire_width) viac.connect(port="e1", destination=wire1.ports["e3"], overlap=(wire_width + via_width) / 2) via1.connect(port="e1", destination=wire2.ports["e3"], overlap=(wire_width + via_width) / 2) c.add_port(name="e1", port=wire1.ports["e1"], port_type="electrical") c.add_port(name="e3", port=wire2.ports["e3"], port_type="electrical") c.add_port( name="e4", midpoint=[(1 * wire_width) + wire_width / 2, -wire_width / 2], width=wire_width, orientation=-90, port_type="electrical", ) c.add_port( name="e2", midpoint=[(1 * wire_width) + wire_width / 2, wire_width / 2], width=wire_width, orientation=90, port_type="electrical", ) return c
def rectangle( size: Tuple[float, float] = (4.0, 2.0), layer: Layer = (1, 0), centered: bool = False, port_type: str = "electrical", ) -> Component: """rectangle Args: size: (tuple) Width and height of rectangle. layer: Specific layer to put polygon geometry on. centered: True sets center to (0, 0), False sets south-west to (0, 0) port_type: """ c = Component() ref = c << compass(size=size, layer=layer, port_type=port_type) if not centered: ref.move((size[0] / 2, size[1] / 2)) c.add_ports(ref.ports) return c
def contact_slot( size: Float2 = (11.0, 11.0), layers: Layers = (LAYER.M1, LAYER.M2), layer_offsets: Optional[Floats] = (0, 1.0), layer_offsetsx: Optional[Floats] = None, layer_offsetsy: Optional[Floats] = None, layer_port: Optional[Layer] = None, via: ComponentOrFactory = via1, enclosure: float = 1.0, ysize: float = 0.5, yspacing: float = 2.0, ) -> Component: """Rectangular contact with slotted via in X direction Args: size: of the layers layers: layers on which to draw rectangles layer_offsets: cladding_offset for each layer layer_offsetsx: optional xoffset for layers, defaults to layer_offsets layer_offsetsx: optional yoffset for layers, defaults to layer_offsets layer_port: if None asumes port is on the last layer via: via to use to fill the rectangles enclosure: of the via by rectangle ysize: via height in y yspacing: via spacing pitch in y .. code:: __________________________________________ | | | | | layer_offsetsy[1] | | ______________|______________________ | | |<---> |<>| | |enclosure | layer_offsetsx[1] | | ______________________ | | | | | | | | | | | via | ysize| | | | |______________________| | | | | | | | | | | yspacing size[1]| | | | | | | | | | ______________________ | | | | | | | | | | | | | via | ysize| | | | | |______________________| | | | | | | | | | | | |___________________________________| | | size[0] | | | |_________________________________________| """ if size[0] - 2 * enclosure < 0: raise ValueError( f"Contact length (size[0] = {size[0]}) < 2*enclosure ({2*enclosure}). " ) if size[1] - 2 * enclosure < 0: raise ValueError( f"Contact width (size[1] = {size[1]}) < 2*enclosure ({2*enclosure}). " ) layer_port = layer_port or layers[-1] c = Component() c.info.size = (float(size[0]), float(size[1])) layer_offsetsx = layer_offsetsx or layer_offsets layer_offsetsy = layer_offsetsy or layer_offsets layer_offsetsx = list(layer_offsetsx) + [0] * len(layers) layer_offsetsy = list(layer_offsetsy) + [0] * len(layers) for layer, offsetx, offsety in zip(layers, layer_offsetsx, layer_offsetsy): ref = c << compass(size=(size[0] + 2 * offsetx, size[1] + 2 * offsety), layer=layer) if layer == layer_port: c.add_ports(ref.ports) via = via(size=(size[0] - 2 * enclosure, ysize)) if callable(via) else via nb_vias_y = (size[1] - 2 * enclosure) / yspacing nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array(via, columns=1, rows=nb_vias_y, spacing=(0, yspacing)) dy = (size[1] - (nb_vias_y - 1) * yspacing - size[1]) / 2 ref.move((0, dy)) return c
def contact( size: Tuple[float, float] = (11.0, 11.0), layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2, LAYER.M3), vias: Optional[Tuple[Optional[ComponentOrFactory], ...]] = (via1, via2), layer_port: Optional[Layer] = None, ) -> Component: """Rectangular via array stack You can use it to connect different metal layers or metals to silicon. You can use the naming convention contact_layerSource_layerDestination Via array / stack name is more common for contacting metal while contact is used for contacting silicon http://www.vlsi-expert.com/2017/12/vias.html Args: size: of the layers layers: layers on which to draw rectangles vias: vias to use to fill the rectangles layer_port: if None asumes port is on the last layer """ width, height = size a = width / 2 b = height / 2 layer_port = layer_port or layers[-1] c = Component() c.height = height c.info.size = (float(size[0]), float(size[1])) c.info.layer = layer_port for layer in layers: ref = c << compass(size=(width, height), layer=layer) if layer == layer_port: c.add_ports(ref.ports) vias = vias or [] for via in vias: if via is not None: via = via() if callable(via) else via w, h = via.info["size"] g = via.info["enclosure"] pitch_x, pitch_y = via.info["spacing"] nb_vias_x = (width - w - 2 * g) / pitch_x + 1 nb_vias_y = (height - h - 2 * g) / pitch_y + 1 nb_vias_x = int(floor(nb_vias_x)) or 1 nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array(via, columns=nb_vias_x, rows=nb_vias_y, spacing=(pitch_x, pitch_y)) cw = (width - (nb_vias_x - 1) * pitch_x - w) / 2 ch = (height - (nb_vias_y - 1) * pitch_y - h) / 2 x0 = -a + cw + w / 2 y0 = -b + ch + h / 2 ref.move((x0, y0)) return c
def contact_slot( size: Tuple[float, float] = (11.0, 11.0), layers: Tuple[Layer, ...] = (LAYER.M1, LAYER.M2), layer_offsets: Tuple[float, ...] = (0, 1.0), layer_port: Optional[Layer] = None, via: ComponentOrFactory = via1, enclosure: float = 1.0, ysize: float = 0.5, yspacing: float = 2.0, ) -> Component: """Rectangular contact with slotted via in X direction Args: size: of the layers layers: layers on which to draw rectangles layer_offsets: cladding_offset for each layer layer_port: if None asumes port is on the last layer via: via to use to fill the rectangles enclosure: of the via by rectangle ysize: via height in y yspacing: via spacing pitch in y .. code:: enclosure _____________________________________ |<---> | | ______________________ | | | | | | | | ysize| | |______________________| | | | | | | yspacing | | | | | | ______________________ | | | | | | | | | | ysize| | | |______________________| | | | |___________________________________| size[0] """ layer_port = layer_port or layers[-1] c = Component() for layer, offset in zip(layers, list(layer_offsets) + [0] * len(layers)): ref = c << compass(size=(size[0] + 2 * offset, size[1] + 2 * offset), layer=layer) if layer == layer_port: c.add_ports(ref.ports) via = via(size=(size[0] - 2 * enclosure, ysize)) if callable(via) else via nb_vias_y = (size[1] - 2 * enclosure) / yspacing nb_vias_y = int(floor(nb_vias_y)) or 1 ref = c.add_array(via, columns=1, rows=nb_vias_y, spacing=(0, yspacing)) dy = (size[1] - (nb_vias_y - 1) * yspacing - size[1]) / 2 ref.move((0, dy)) return c