diff --git a/src/glayout/blocks/ATLAS/current_mirror.py b/src/glayout/blocks/ATLAS/current_mirror.py deleted file mode 100644 index 3d7bf11f..00000000 --- a/src/glayout/blocks/ATLAS/current_mirror.py +++ /dev/null @@ -1,223 +0,0 @@ -from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.spice.netlist import Netlist -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as sky130 -from glayout.flow.primitives.fet import nmos, pmos -from glayout.flow.primitives.guardring import tapring -from glayout.flow.pdk.util.port_utils import add_ports_perimeter,rename_ports_by_orientation -from gdsfactory.component import Component -from gdsfactory.cell import cell -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port -from typing import Optional, Union -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk -from glayout.flow.primitives.via_gen import via_stack -from gdsfactory.components import text_freetype, rectangle - -try: - from evaluator_wrapper import run_evaluation -except ImportError: - print("Warning: evaluator_wrapper not found. Evaluation will be skipped.") - run_evaluation = None - -def add_cm_labels(cm_in: Component, - pdk: MappedPDK - ) -> Component: - - cm_in.unlock() - met2_pin = (68,16) - met2_label = (68,5) - - # list that will contain all port/comp info - move_info = list() - # create labels and append to info list - # vss - vsslabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vsslabel.add_label(text="VSS",layer=pdk.get_glayer("met2_label")) - move_info.append((vsslabel,cm_in.ports["fet_A_source_E"],None)) - - # vref - vreflabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vreflabel.add_label(text="VREF",layer=pdk.get_glayer("met2_label")) - move_info.append((vreflabel,cm_in.ports["fet_A_drain_N"],None)) - - # vcopy - vcopylabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vcopylabel.add_label(text="VCOPY",layer=pdk.get_glayer("met2_label")) - move_info.append((vcopylabel,cm_in.ports["fet_B_drain_N"],None)) - - # VB - vblabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.5,0.5),centered=True).copy() - vblabel.add_label(text="VB",layer=pdk.get_glayer("met2_label")) - move_info.append((vblabel,cm_in.ports["welltie_S_top_met_S"], None)) - - # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - compref = align_comp_to_port(comp, prt, alignment=alignment) - cm_in.add(compref) - return cm_in.flatten() - -def current_mirror_netlist( - pdk: MappedPDK, - width: float, - length: float, - multipliers: int, - with_dummy: bool = True, - n_or_p_fet: Optional[str] = 'nfet', - subckt_only: Optional[bool] = False -) -> Netlist: - if length is None: - length = pdk.get_grule('poly')['min_width'] - if width is None: - width = 3 - mtop = multipliers if subckt_only else 1 - model = pdk.models[n_or_p_fet] - - source_netlist = """.subckt {circuit_name} {nodes} """ + f'l={length} w={width} m={mtop} ' + """ -XA VREF VREF VSS VB {model} l={{l}} w={{w}} m={{m}} -XB VCOPY VREF VSS VB {model} l={{l}} w={{w}} m={{m}}""" - if with_dummy: - source_netlist += "\nXDUMMY VB VB VB VB {model} l={{l}} w={{w}} m={{2}}" - source_netlist += "\n.ends {circuit_name}" - - instance_format = "X{name} {nodes} {circuit_name} l={length} w={width} m={mult}" - - return Netlist( - circuit_name='CMIRROR', - nodes=['VREF', 'VCOPY', 'VSS', 'VB'], - source_netlist=source_netlist, - instance_format=instance_format, - parameters={ - 'model': model, - 'width': width, - 'length': length, - 'mult': multipliers - } - ) - - -#@cell -def current_mirror( - pdk: MappedPDK, - numcols: int = 3, - device: str = 'nfet', - with_dummy: Optional[bool] = True, - with_substrate_tap: Optional[bool] = False, - with_tie: Optional[bool] = True, - tie_layers: tuple[str,str]=("met2","met1"), - **kwargs -) -> Component: - """An instantiable current mirror that returns a Component object. The current mirror is a two transistor interdigitized structure with a shorted source and gate. It can be instantiated with either nmos or pmos devices. It can also be instantiated with a dummy device, a substrate tap, and a tie layer, and is centered at the origin. Transistor A acts as the reference and Transistor B acts as the mirror fet - - Args: - pdk (MappedPDK): the process design kit to use - numcols (int): number of columns of the interdigitized fets - device (str): nfet or pfet (can only interdigitize one at a time with this option) - with_dummy (bool): True places dummies on either side of the interdigitized fets - with_substrate_tap (bool): boolean to decide whether to place a substrate tapring - with_tie (bool): boolean to decide whether to place a tapring for tielayer - tie_layers (tuple[str,str], optional): the layers to use for the tie. Defaults to ("met2","met1"). - **kwargs: The keyword arguments are passed to the two_nfet_interdigitized or two_pfet_interdigitized functions and need to be valid arguments that can be accepted by the multiplier function - - Returns: - Component: a current mirror component object - """ - top_level = Component("current mirror") - if device in ['nmos', 'nfet']: - interdigitized_fets = two_nfet_interdigitized( - pdk, - numcols=numcols, - dummy=with_dummy, - with_substrate_tap=False, - with_tie=False, - **kwargs - ) - elif device in ['pmos', 'pfet']: - interdigitized_fets = two_pfet_interdigitized( - pdk, - numcols=numcols, - dummy=with_dummy, - with_substrate_tap=False, - with_tie=False, - **kwargs - ) - top_level.add_ports(interdigitized_fets.get_ports_list(), prefix="fet_") - maxmet_sep = pdk.util_max_metal_seperation() - # short source of the fets - source_short = interdigitized_fets << c_route(pdk, interdigitized_fets.ports['A_source_E'], interdigitized_fets.ports['B_source_E'], extension=3*maxmet_sep, viaoffset=False) - # short gates of the fets - gate_short = interdigitized_fets << c_route(pdk, interdigitized_fets.ports['A_gate_W'], interdigitized_fets.ports['B_gate_W'], extension=3*maxmet_sep, viaoffset=False) - # short gate and drain of one of the reference - interdigitized_fets << L_route(pdk, interdigitized_fets.ports['A_drain_W'], gate_short.ports['con_N'], viaoffset=False, fullbottom=False) - - top_level << interdigitized_fets - if with_tie: - if device in ['nmos','nfet']: - tap_layer = "p+s/d" - if device in ['pmos','pfet']: - tap_layer = "n+s/d" - tap_sep = max( - float(pdk.util_max_metal_seperation()), - float(pdk.get_grule("active_diff", "active_tap")["min_separation"]), - ) - tap_sep += float(pdk.get_grule(tap_layer, "active_tap")["min_enclosure"]) - tap_encloses = ( - 2 * (tap_sep + interdigitized_fets.xmax), - 2 * (tap_sep + interdigitized_fets.ymax), - ) - tie_ref = top_level << tapring(pdk, enclosed_rectangle = tap_encloses, sdlayer = tap_layer, horizontal_glayer = tie_layers[0], vertical_glayer = tie_layers[1]) - top_level.add_ports(tie_ref.get_ports_list(), prefix="welltie_") - try: - top_level << straight_route(pdk, top_level.ports[f"fet_B_{numcols - 1}_dummy_R_gsdcon_top_met_E"],top_level.ports["welltie_E_top_met_E"],glayer2="met1") - top_level << straight_route(pdk, top_level.ports["fet_A_0_dummy_L_gsdcon_top_met_W"],top_level.ports["welltie_W_top_met_W"],glayer2="met1") - except KeyError: - pass - try: - end_col = numcols - 1 - port1 = f'B_{end_col}_dummy_R_gdscon_top_met_E' - top_level << straight_route(pdk, top_level.ports[port1], top_level.ports["welltie_E_top_met_E"], glayer2="met1") - except KeyError: - pass - - # add a pwell - if device in ['nmos','nfet']: - top_level.add_padding(layers = (pdk.get_glayer("pwell"),), default = pdk.get_grule("pwell", "active_tap")["min_enclosure"], ) - top_level = add_ports_perimeter(top_level, layer = pdk.get_glayer("pwell"), prefix="well_") - if device in ['pmos','pfet']: - top_level.add_padding(layers = (pdk.get_glayer("nwell"),), default = pdk.get_grule("nwell", "active_tap")["min_enclosure"], ) - top_level = add_ports_perimeter(top_level, layer = pdk.get_glayer("nwell"), prefix="well_") - - - # add the substrate tap if specified - if with_substrate_tap: - subtap_sep = pdk.get_grule("dnwell", "active_tap")["min_separation"] - subtap_enclosure = ( - 2.5 * (subtap_sep + interdigitized_fets.xmax), - 2.5 * (subtap_sep + interdigitized_fets.ymax), - ) - subtap_ring = top_level << tapring(pdk, enclosed_rectangle = subtap_enclosure, sdlayer = "p+s/d", horizontal_glayer = "met2", vertical_glayer = "met1") - top_level.add_ports(subtap_ring.get_ports_list(), prefix="substrate_tap_") - - top_level.add_ports(source_short.get_ports_list(), prefix='purposegndports') - - - top_level.info['netlist'] = current_mirror_netlist( - pdk, - width=kwargs.get('width', 3), length=kwargs.get('length', 0.15), multipliers=numcols, with_dummy=with_dummy, - n_or_p_fet=device, - subckt_only=True - ) - - return top_level - -if __name__=="__main__": - current_mirror = add_cm_labels(current_mirror(sky130_mapped_pdk, device='pfet'),sky130_mapped_pdk) - current_mirror.show() - current_mirror.name = "CMIRROR" - #magic_drc_result = sky130_mapped_pdk.drc_magic(current_mirror, current_mirror.name) - #netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(current_mirror, current_mirror.name) - current_mirror_gds = current_mirror.write_gds("current_mirror.gds") - res = run_evaluation("current_mirror.gds", current_mirror.name, current_mirror) \ No newline at end of file diff --git a/src/glayout/blocks/ATLAS/debug_netlist.py b/src/glayout/blocks/ATLAS/debug_netlist.py deleted file mode 100644 index 05e322f7..00000000 --- a/src/glayout/blocks/ATLAS/debug_netlist.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -""" -Debug script to investigate the netlist reconstruction issue. -""" - -import sys -import os - -# Add the glayout path -glayout_path = "/home/arnavshukla/OpenFASOC/openfasoc/generators/glayout" -if glayout_path not in sys.path: - sys.path.insert(0, glayout_path) - -# Set up environment -os.environ['PDK_ROOT'] = '/opt/conda/envs/GLdev/share/pdk' -os.environ['PDK'] = 'sky130A' - -def debug_netlist_storage(): - """Debug what's actually being stored in component.info""" - print("šŸ” Debugging Netlist Storage...") - - from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk - from transmission_gate import transmission_gate - - pdk = sky130_mapped_pdk - - print("šŸ“‹ Creating transmission gate...") - tg = transmission_gate(pdk=pdk, width=(1.0, 2.0), length=(0.15, 0.15)) - - print("\nšŸ“Š Component Info Contents:") - print("Keys:", list(tg.info.keys())) - - for key, value in tg.info.items(): - print(f"\n{key}: {type(value)}") - if isinstance(value, str): - print(f" Length: {len(value)}") - print(f" Preview: {value[:100]}...") - elif isinstance(value, dict): - print(f" Dict keys: {list(value.keys())}") - for k, v in value.items(): - print(f" {k}: {type(v)} - {str(v)[:50]}...") - - # Test reconstruction - print("\nšŸ”§ Testing Reconstruction...") - if 'netlist_data' in tg.info: - from glayout.flow.spice.netlist import Netlist - data = tg.info['netlist_data'] - print(f"Netlist data: {data}") - - try: - netlist_obj = Netlist( - circuit_name=data['circuit_name'], - nodes=data['nodes'] - ) - netlist_obj.source_netlist = data['source_netlist'] - - print(f"Reconstructed netlist object: {netlist_obj}") - print(f"Circuit name: {netlist_obj.circuit_name}") - print(f"Nodes: {netlist_obj.nodes}") - print(f"Source netlist: {netlist_obj.source_netlist}") - - generated = netlist_obj.generate_netlist() - print(f"Generated netlist length: {len(generated)}") - print(f"Generated content:\n{generated}") - - except Exception as e: - print(f"Error reconstructing: {e}") - import traceback - traceback.print_exc() - -if __name__ == "__main__": - debug_netlist_storage() diff --git a/src/glayout/blocks/ATLAS/debug_sample_11.py b/src/glayout/blocks/ATLAS/debug_sample_11.py deleted file mode 100644 index 1dd3c00b..00000000 --- a/src/glayout/blocks/ATLAS/debug_sample_11.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python3 -""" -Debug script for sample 11 that was hanging -""" - -import sys -import time -import json -from pathlib import Path - -# Add glayout to path -_here = Path(__file__).resolve() -_root_dir = _here.parent.parent.parent.parent.parent -sys.path.insert(0, str(_root_dir)) - -from glayout.flow.blocks.elementary.LHS.transmission_gate import transmission_gate, add_tg_labels -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk - -def test_sample_11(): - """Test the specific parameters that are causing sample 11 to hang""" - - # Sample 11 parameters (index 10) - params = { - "width": [15.56987768790995, 19.431313875884364], - "length": [2.2925198967864566, 0.8947369421533957], - "fingers": [5, 5], - "multipliers": [2, 2] - } - - print("Testing sample 11 parameters:") - print(f"Parameters: {params}") - - # Convert to tuples - width_tuple = tuple(params['width']) - length_tuple = tuple(params['length']) - fingers_tuple = tuple(params['fingers']) - multipliers_tuple = tuple(params['multipliers']) - - print(f"Width tuple: {width_tuple}") - print(f"Length tuple: {length_tuple}") - print(f"Fingers tuple: {fingers_tuple}") - print(f"Multipliers tuple: {multipliers_tuple}") - - try: - print("Creating transmission gate...") - start_time = time.time() - - tg_component = transmission_gate( - pdk=sky130_mapped_pdk, - width=width_tuple, - length=length_tuple, - fingers=fingers_tuple, - multipliers=multipliers_tuple, - substrate_tap=True - ) - - creation_time = time.time() - start_time - print(f"āœ… Transmission gate created in {creation_time:.2f}s") - - print("Adding labels...") - start_time = time.time() - cell = add_tg_labels(tg_component, sky130_mapped_pdk) - cell.name = "test_sample_11" - label_time = time.time() - start_time - print(f"āœ… Labels added in {label_time:.2f}s") - - print("Writing GDS...") - start_time = time.time() - cell.write_gds("test_sample_11.gds") - gds_time = time.time() - start_time - print(f"āœ… GDS written in {gds_time:.2f}s") - - print("šŸŽ‰ Sample 11 test completed successfully!") - - except Exception as e: - print(f"āŒ Error: {e}") - import traceback - traceback.print_exc() - -if __name__ == "__main__": - test_sample_11() \ No newline at end of file diff --git a/src/glayout/blocks/ATLAS/diff_pair.py b/src/glayout/blocks/ATLAS/diff_pair.py deleted file mode 100644 index 116a58cd..00000000 --- a/src/glayout/blocks/ATLAS/diff_pair.py +++ /dev/null @@ -1,257 +0,0 @@ -from typing import Optional, Union - -from gdsfactory.cell import cell -from gdsfactory.component import Component, copy -from gdsfactory.components.rectangle import rectangle -from gdsfactory.routing.route_quad import route_quad -from gdsfactory.routing.route_sharp import route_sharp -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.util.comp_utils import align_comp_to_port, evaluate_bbox, movex, movey -from glayout.flow.pdk.util.port_utils import ( - add_ports_perimeter, - get_orientation, - print_ports, - rename_ports_by_list, - rename_ports_by_orientation, - set_port_orientation, -) -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.flow.placement.common_centroid_ab_ba import common_centroid_ab_ba -from glayout.flow.primitives.fet import nmos, pmos -from glayout.flow.primitives.guardring import tapring -from glayout.flow.primitives.via_gen import via_stack -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.smart_route import smart_route -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.spice import Netlist -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk -from gdsfactory.components import text_freetype -try: - from evaluator_wrapper import run_evaluation -except ImportError: - print("Warning: evaluator_wrapper not found. Evaluation will be skipped.") - run_evaluation = None - - -def add_df_labels(df_in: Component, - pdk: MappedPDK - ) -> Component: - - df_in.unlock() - met1_pin = (67,16) - met1_label = (67,5) - met2_pin = (68,16) - met2_label = (68,5) - # list that will contain all port/comp info - move_info = list() - # create labels and append to info list - # vtail - vtaillabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vtaillabel.add_label(text="VTAIL",layer=pdk.get_glayer("met2_label")) - move_info.append((vtaillabel,df_in.ports["bl_multiplier_0_source_S"],None)) - - # vdd1 - vdd1label = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vdd1label.add_label(text="VDD1",layer=pdk.get_glayer("met2_label")) - move_info.append((vdd1label,df_in.ports["tl_multiplier_0_drain_N"],None)) - - # vdd2 - vdd2label = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vdd2label.add_label(text="VDD2",layer=pdk.get_glayer("met2_label")) - move_info.append((vdd2label,df_in.ports["tr_multiplier_0_drain_N"],None)) - - # VB - vblabel = rectangle(layer=pdk.get_glayer("met1_pin"),size=(0.5,0.5),centered=True).copy() - vblabel.add_label(text="B",layer=pdk.get_glayer("met1_label")) - move_info.append((vblabel,df_in.ports["tap_N_top_met_S"], None)) - - # VP - vplabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vplabel.add_label(text="VP",layer=pdk.get_glayer("met2_label")) - move_info.append((vplabel,df_in.ports["br_multiplier_0_gate_S"], None)) - - # VN - vnlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vnlabel.add_label(text="VN",layer=pdk.get_glayer("met2_label")) - move_info.append((vnlabel,df_in.ports["bl_multiplier_0_gate_S"], None)) - - # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - compref = align_comp_to_port(comp, prt, alignment=alignment) - df_in.add(compref) - return df_in.flatten() - -def diff_pair_netlist(fetL: Component, fetR: Component) -> Netlist: - diff_pair_netlist = Netlist(circuit_name='DIFF_PAIR', nodes=['VP', 'VN', 'VDD1', 'VDD2', 'VTAIL', 'B']) - diff_pair_netlist.connect_netlist( - fetL.info['netlist'], - [('D', 'VDD1'), ('G', 'VP'), ('S', 'VTAIL'), ('B', 'B')] - ) - diff_pair_netlist.connect_netlist( - fetR.info['netlist'], - [('D', 'VDD2'), ('G', 'VN'), ('S', 'VTAIL'), ('B', 'B')] - ) - return diff_pair_netlist - -@cell -def diff_pair( - pdk: MappedPDK, - width: float = 3, - fingers: int = 4, - length: Optional[float] = None, - n_or_p_fet: bool = True, - plus_minus_seperation: float = 0, - rmult: int = 1, - dummy: Union[bool, tuple[bool, bool]] = True, - substrate_tap: bool=True -) -> Component: - """create a diffpair with 2 transistors placed in two rows with common centroid place. Sources are shorted - width = width of the transistors - fingers = number of fingers in the transistors (must be 2 or more) - length = length of the transistors, None or 0 means use min length - short_source = if true connects source of both transistors - n_or_p_fet = if true the diffpair is made of nfets else it is made of pfets - substrate_tap: if true place a tapring around the diffpair (connects on met1) - """ - # TODO: error checking - pdk.activate() - diffpair = Component() - # create transistors - well = None - if isinstance(dummy, bool): - dummy = (dummy, dummy) - if n_or_p_fet: - fetL = nmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(dummy[0], False),with_dnwell=False,with_substrate_tap=False,rmult=rmult) - fetR = nmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(False,dummy[1]),with_dnwell=False,with_substrate_tap=False,rmult=rmult) - min_spacing_x = pdk.get_grule("n+s/d")["min_separation"] - 2*(fetL.xmax - fetL.ports["multiplier_0_plusdoped_E"].center[0]) - well = "pwell" - else: - fetL = pmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(dummy[0], False),dnwell=False,with_substrate_tap=False,rmult=rmult) - fetR = pmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(False,dummy[1]),dnwell=False,with_substrate_tap=False,rmult=rmult) - min_spacing_x = pdk.get_grule("p+s/d")["min_separation"] - 2*(fetL.xmax - fetL.ports["multiplier_0_plusdoped_E"].center[0]) - well = "nwell" - # place transistors - viam2m3 = via_stack(pdk,"met2","met3",centered=True) - metal_min_dim = max(pdk.get_grule("met2")["min_width"],pdk.get_grule("met3")["min_width"]) - metal_space = max(pdk.get_grule("met2")["min_separation"],pdk.get_grule("met3")["min_separation"],metal_min_dim) - gate_route_os = evaluate_bbox(viam2m3)[0] - fetL.ports["multiplier_0_gate_W"].width + metal_space - min_spacing_y = metal_space + 2*gate_route_os - min_spacing_y = min_spacing_y - 2*abs(fetL.ports["well_S"].center[1] - fetL.ports["multiplier_0_gate_S"].center[1]) - # TODO: fix spacing where you see +-0.5 - a_topl = (diffpair << fetL).movey(fetL.ymax+min_spacing_y/2+0.5).movex(0-fetL.xmax-min_spacing_x/2) - b_topr = (diffpair << fetR).movey(fetR.ymax+min_spacing_y/2+0.5).movex(fetL.xmax+min_spacing_x/2) - a_botr = (diffpair << fetR) - a_botr = a_botr.mirror_y() - a_botr.movey(0-0.5-fetL.ymax-min_spacing_y/2).movex(fetL.xmax+min_spacing_x/2) - b_botl = (diffpair << fetL) - b_botl = b_botl.mirror_y() - b_botl.movey(0-0.5-fetR.ymax-min_spacing_y/2).movex(0-fetL.xmax-min_spacing_x/2) - # if substrate tap place substrate tap - if substrate_tap: - tapref = diffpair << tapring(pdk,evaluate_bbox(diffpair,padding=1),horizontal_glayer="met1") - diffpair.add_ports(tapref.get_ports_list(),prefix="tap_") - try: - diffpair< Component: - diffpair = common_centroid_ab_ba(pdk,width,fingers,length,n_or_p_fet,rmult,dummy,substrate_tap) - diffpair << smart_route(pdk,diffpair.ports["A_source_E"],diffpair.ports["B_source_E"],diffpair, diffpair) - return diffpair - -if __name__=="__main__": - diff_pair = add_df_labels(diff_pair(sky130_mapped_pdk),sky130_mapped_pdk) - #diff_pair = diff_pair(sky130_mapped_pdk) - diff_pair.show() - diff_pair.name = "DIFF_PAIR" - #magic_drc_result = sky130_mapped_pdk.drc_magic(diff_pair, diff_pair.name) - #netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(diff_pair, diff_pair.name) - diff_pair_gds = diff_pair.write_gds("diff_pair.gds") - res = run_evaluation("diff_pair.gds", diff_pair.name, diff_pair) \ No newline at end of file diff --git a/src/glayout/blocks/ATLAS/evaluator_wrapper.py b/src/glayout/blocks/ATLAS/evaluator_wrapper.py index d378794a..cda1c13f 100644 --- a/src/glayout/blocks/ATLAS/evaluator_wrapper.py +++ b/src/glayout/blocks/ATLAS/evaluator_wrapper.py @@ -8,7 +8,7 @@ from gdsfactory.typings import Component from robust_verification import run_robust_verification -from glayout.flow.blocks.evaluator_box.physical_features import run_physical_feature_extraction +from glayout.blocks.evaluator_box.physical_features import run_physical_feature_extraction def get_next_filename(base_name="evaluation", extension=".json"): """ diff --git a/src/glayout/blocks/ATLAS/fvf.py b/src/glayout/blocks/ATLAS/fvf.py deleted file mode 100644 index 106a932d..00000000 --- a/src/glayout/blocks/ATLAS/fvf.py +++ /dev/null @@ -1,205 +0,0 @@ -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk -from gdsfactory.cell import cell -from gdsfactory.component import Component -from gdsfactory import Component -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.primitives.guardring import tapring -from glayout.flow.pdk.util.port_utils import add_ports_perimeter -from glayout.flow.spice.netlist import Netlist -from glayout.flow.primitives.via_gen import via_stack -from gdsfactory.components import text_freetype, rectangle -from evaluator_wrapper import run_evaluation # CUSTOM IMPLEMENTED EVAL BOX - -def get_component_netlist(component): - """Helper function to get netlist object from component info, compatible with all gdsfactory versions""" - from glayout.flow.spice.netlist import Netlist - - # Try to get stored object first (for older gdsfactory versions) - if 'netlist_obj' in component.info: - return component.info['netlist_obj'] - - # Try to reconstruct from netlist_data (for newer gdsfactory versions) - if 'netlist_data' in component.info: - data = component.info['netlist_data'] - netlist = Netlist( - circuit_name=data['circuit_name'], - nodes=data['nodes'] - ) - netlist.source_netlist = data['source_netlist'] - return netlist - - # Fallback: return the string representation (should not happen in normal operation) - return component.info.get('netlist', '') - -def fvf_netlist(fet_1: Component, fet_2: Component) -> Netlist: - - netlist = Netlist(circuit_name='FLIPPED_VOLTAGE_FOLLOWER', nodes=['VIN', 'VBULK', 'VOUT', 'Ib']) - - # Use helper function to get netlist objects regardless of gdsfactory version - fet_1_netlist = get_component_netlist(fet_1) - fet_2_netlist = get_component_netlist(fet_2) - netlist.connect_netlist(fet_1_netlist, [('D', 'Ib'), ('G', 'VIN'), ('S', 'VOUT'), ('B', 'VBULK')]) - netlist.connect_netlist(fet_2_netlist, [('D', 'VOUT'), ('G', 'Ib'), ('S', 'VBULK'), ('B', 'VBULK')]) - - return netlist - -def sky130_add_fvf_labels(fvf_in: Component) -> Component: - - fvf_in.unlock() - # define layers` - met1_pin = (68,16) - met1_label = (68,5) - met2_pin = (69,16) - met2_label = (69,5) - # list that will contain all port/comp info - move_info = list() - # create labels and append to info list - # gnd - gnd2label = rectangle(layer=met1_pin,size=(0.5,0.5),centered=True).copy() - gnd2label.add_label(text="VBULK",layer=met1_label) - move_info.append((gnd2label,fvf_in.ports["B_tie_N_top_met_N"],None)) - - #currentbias - ibiaslabel = rectangle(layer=met2_pin,size=(0.5,0.5),centered=True).copy() - ibiaslabel.add_label(text="Ib",layer=met2_label) - move_info.append((ibiaslabel,fvf_in.ports["A_drain_bottom_met_N"],None)) - - # output (3rd stage) - outputlabel = rectangle(layer=met2_pin,size=(0.5,0.5),centered=True).copy() - outputlabel.add_label(text="VOUT",layer=met2_label) - move_info.append((outputlabel,fvf_in.ports["A_source_bottom_met_N"],None)) - - # input - inputlabel = rectangle(layer=met1_pin,size=(0.5,0.5),centered=True).copy() - inputlabel.add_label(text="VIN",layer=met1_label) - move_info.append((inputlabel,fvf_in.ports["A_multiplier_0_gate_N"], None)) - - # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - compref = align_comp_to_port(comp, prt, alignment=alignment) - fvf_in.add(compref) - return fvf_in.flatten() - -@cell -def flipped_voltage_follower( - pdk: MappedPDK, - device_type: str = "nmos", - placement: str = "horizontal", - width: tuple[float,float] = (6.605703928526579, 3.713220935212418), - length: tuple[float,float] = (2.3659471990041707, 1.9639325665440608), - fingers: tuple[int,int] = (1, 1), - multipliers: tuple[int,int] = (2, 2), - dummy_1: tuple[bool,bool] = (True,True), - dummy_2: tuple[bool,bool] = (True,True), - tie_layers1: tuple[str,str] = ("met2","met1"), - tie_layers2: tuple[str,str] = ("met2","met1"), - sd_rmult: int=1, - **kwargs - ) -> Component: - """ - creates a Flipped Voltage Follower - pdk: pdk to use - device_type: either "nmos" or "pmos" - placement: either "horizontal" or "vertical" - width: (input fet, feedback fet) - length: (input fet, feedback fet) - fingers: (input fet, feedback fet) - multipliers: (input fet, feedback fet) - dummy_1: dummy for input fet - dummy_2: dummy for feedback fet - tie_layers1: tie layers for input fet - tie_layers2: tie layers for feedback fet - sd_rmult: sd_rmult for both fets - **kwargs: any kwarg that is supported by nmos and pmos - """ - - #top level component - top_level = Component(name="flipped_voltage_follower") - - #two fets - device_map = { - "nmos": nmos, - "pmos": pmos, - } - device = device_map.get(device_type) - - if device_type == "nmos": - kwargs["with_dnwell"] = False # Set the parameter dynamically - - - fet_1 = device(pdk, width=width[0], fingers=fingers[0], multipliers=multipliers[0], with_dummy=dummy_1, with_substrate_tap=False, length=length[0], tie_layers=tie_layers1, sd_rmult=sd_rmult, **kwargs) - fet_2 = device(pdk, width=width[1], fingers=fingers[1], multipliers=multipliers[1], with_dummy=dummy_2, with_substrate_tap=False, length=length[1], tie_layers=tie_layers2, sd_rmult=sd_rmult, **kwargs) - well = "pwell" if device == nmos else "nwell" - fet_1_ref = top_level << fet_1 - fet_2_ref = top_level << fet_2 - - #Relative move - ref_dimensions = evaluate_bbox(fet_2) - if placement == "horizontal": - fet_2_ref.movex(fet_1_ref.xmax + ref_dimensions[0]/2 + pdk.util_max_metal_seperation()-0.5) - if placement == "vertical": - fet_2_ref.movey(fet_1_ref.ymin - ref_dimensions[1]/2 - pdk.util_max_metal_seperation()-1) - - #Routing - viam2m3 = via_stack(pdk, "met2", "met3", centered=True) - drain_1_via = top_level << viam2m3 - source_1_via = top_level << viam2m3 - drain_2_via = top_level << viam2m3 - gate_2_via = top_level << viam2m3 - drain_1_via.move(fet_1_ref.ports["multiplier_0_drain_W"].center).movex(-0.5*evaluate_bbox(fet_1)[1]) - source_1_via.move(fet_1_ref.ports["multiplier_0_source_E"].center).movex(1.5) - drain_2_via.move(fet_2_ref.ports["multiplier_0_drain_W"].center).movex(-1.5) - gate_2_via.move(fet_2_ref.ports["multiplier_0_gate_E"].center).movex(1) - - top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_source_E"], source_1_via.ports["bottom_met_W"]) - top_level << straight_route(pdk, fet_2_ref.ports["multiplier_0_drain_W"], drain_2_via.ports["bottom_met_E"]) - top_level << c_route(pdk, source_1_via.ports["top_met_N"], drain_2_via.ports["top_met_N"], extension=1.2*max(width[0],width[1]), e1glayer="met3", e2glayer="met3", cglayer="met2") - top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_drain_W"], drain_1_via.ports["bottom_met_E"]) - top_level << c_route(pdk, drain_1_via.ports["top_met_S"], gate_2_via.ports["top_met_S"], extension=1.2*max(width[0],width[1]), cglayer="met2") - top_level << straight_route(pdk, fet_2_ref.ports["multiplier_0_gate_E"], gate_2_via.ports["bottom_met_W"]) - try: - top_level << straight_route(pdk, fet_2_ref.ports["multiplier_0_source_W"], fet_2_ref.ports["tie_W_top_met_W"], glayer1=tie_layers2[1], width=0.2*sd_rmult, fullbottom=True) - except: - pass - #Renaming Ports - top_level.add_ports(fet_1_ref.get_ports_list(), prefix="A_") - top_level.add_ports(fet_2_ref.get_ports_list(), prefix="B_") - top_level.add_ports(drain_1_via.get_ports_list(), prefix="A_drain_") - top_level.add_ports(source_1_via.get_ports_list(), prefix="A_source_") - top_level.add_ports(drain_2_via.get_ports_list(), prefix="B_drain_") - top_level.add_ports(gate_2_via.get_ports_list(), prefix="B_gate_") - #add nwell - if well == "nwell": - top_level.add_padding(layers=(pdk.get_glayer("nwell"),),default= 1 ) - - component = component_snap_to_grid(rename_ports_by_orientation(top_level)) - #component = rename_ports_by_orientation(top_level) - - # Store netlist as string to avoid gymnasium info dict type restrictions - # Compatible with both gdsfactory 7.7.0 and 7.16.0+ strict Pydantic validation - netlist_obj = fvf_netlist(fet_1, fet_2) - component.info['netlist'] = str(netlist_obj) - # Store serialized netlist data for reconstruction if needed - component.info['netlist_data'] = { - 'circuit_name': netlist_obj.circuit_name, - 'nodes': netlist_obj.nodes, - 'source_netlist': netlist_obj.source_netlist - } - - return component - -if __name__=="__main__": - fvf = sky130_add_fvf_labels(flipped_voltage_follower(sky130_mapped_pdk, width=(2,1), sd_rmult=3)) - fvf.show() - fvf.name = "fvf" - fvf_gds = fvf.write_gds("fvf.gds") - result = run_evaluation("fvf.gds",fvf.name,fvf) - print(result) \ No newline at end of file diff --git a/src/glayout/blocks/ATLAS/lvcm.py b/src/glayout/blocks/ATLAS/lvcm.py deleted file mode 100644 index 9e85ec6b..00000000 --- a/src/glayout/blocks/ATLAS/lvcm.py +++ /dev/null @@ -1,199 +0,0 @@ -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk -from gdsfactory.component import Component -from gdsfactory.component_reference import ComponentReference -from gdsfactory.cell import cell -from gdsfactory import Component -from gdsfactory.components import text_freetype, rectangle -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, align_comp_to_port, prec_ref_center -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.primitives.guardring import tapring -from glayout.flow.pdk.util.port_utils import add_ports_perimeter -from glayout.flow.spice.netlist import Netlist -from glayout.flow.blocks.elementary.LHS.fvf import fvf_netlist, flipped_voltage_follower -from glayout.flow.primitives.via_gen import via_stack -from typing import Optional -from evaluator_wrapper import run_evaluation - - -def add_lvcm_labels(lvcm_in: Component, - pdk: MappedPDK - ) -> Component: - - lvcm_in.unlock() - - met2_pin = (68,16) - met2_label = (68,5) - met3_pin = (69,16) - met3_label = (69,5) - # list that will contain all port/comp info - move_info = list() - # create labels and append to info list - # gnd - gndlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.5,0.5),centered=True).copy() - gndlabel.add_label(text="GND",layer=pdk.get_glayer("met2_label")) - move_info.append((gndlabel,lvcm_in.ports["M_1_B_tie_N_top_met_N"],None)) - - #currentbias - ibias1label = rectangle(layer=pdk.get_glayer("met3_pin"),size=(0.5,0.5),centered=True).copy() - ibias1label.add_label(text="IBIAS1",layer=pdk.get_glayer("met3_label")) - move_info.append((ibias1label,lvcm_in.ports["M_1_A_drain_bottom_met_N"],None)) - - ibias2label = rectangle(layer=pdk.get_glayer("met3_pin"),size=(0.5,0.5),centered=True).copy() - ibias2label.add_label(text="IBIAS2",layer=pdk.get_glayer("met3_label")) - move_info.append((ibias2label,lvcm_in.ports["M_2_A_drain_bottom_met_N"],None)) - - # output - output1label = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - output1label.add_label(text="IOUT1",layer=pdk.get_glayer("met2_label")) - move_info.append((output1label,lvcm_in.ports["M_3_A_multiplier_0_drain_N"],None)) - - output2label = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - output2label.add_label(text="IOUT2",layer=pdk.get_glayer("met2_label")) - move_info.append((output2label,lvcm_in.ports["M_4_A_multiplier_0_drain_N"],None)) - - # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - compref = align_comp_to_port(comp, prt, alignment=alignment) - lvcm_in.add(compref) - return lvcm_in.flatten() - -def low_voltage_cmirr_netlist(bias_fvf: Component, cascode_fvf: Component, fet_1_ref: ComponentReference, fet_2_ref: ComponentReference, fet_3_ref: ComponentReference, fet_4_ref: ComponentReference) -> Netlist: - - netlist = Netlist(circuit_name='Low_voltage_current_mirror', nodes=['IBIAS1', 'IBIAS2', 'GND', 'IOUT1', 'IOUT2']) - netlist.connect_netlist(bias_fvf.info['netlist'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib','IBIAS1'),('VOUT','local_net_1')]) - netlist.connect_netlist(cascode_fvf.info['netlist'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib', 'IBIAS2'),('VOUT','local_net_2')]) - fet_1A_ref=netlist.connect_netlist(fet_2_ref.info['netlist'], [('D', 'IOUT1'),('G','IBIAS1'),('B','GND')]) - fet_2A_ref=netlist.connect_netlist(fet_4_ref.info['netlist'], [('D', 'IOUT2'),('G','IBIAS1'),('B','GND')]) - fet_1B_ref=netlist.connect_netlist(fet_1_ref.info['netlist'], [('G','IBIAS2'),('S', 'GND'),('B','GND')]) - fet_2B_ref=netlist.connect_netlist(fet_3_ref.info['netlist'], [('G','IBIAS2'),('S', 'GND'),('B','GND')]) - netlist.connect_subnets( - fet_1A_ref, - fet_1B_ref, - [('S', 'D')] - ) - netlist.connect_subnets( - fet_2A_ref, - fet_2B_ref, - [('S', 'D')] - ) - - return netlist - -@cell -def low_voltage_cmirror( - pdk: MappedPDK, - width: tuple[float,float] = (4.15,1.42), - length: float = 2, - fingers: tuple[int,int] = (2,1), - multipliers: tuple[int,int] = (1,1), - ) -> Component: - """ - A low voltage N type current mirror. It has two input brnaches and two output branches. It consists of total 8 nfets, 7 of them have the same W/L. One nfet has width of w' = w/3(theoretcially) - The default values are used to mirror 10uA. - """ - #top level component - top_level = Component("Low_voltage_N-type_current_mirror") - - #input branch 2 - cascode_fvf = flipped_voltage_follower(pdk, width=(width[0],width[0]), length=(length,length), fingers=(fingers[0],fingers[0]), multipliers=(multipliers[0],multipliers[0]), with_dnwell=False) - cascode_fvf_ref = prec_ref_center(cascode_fvf) - top_level.add(cascode_fvf_ref) - - #input branch 1 - bias_fvf = flipped_voltage_follower(pdk, width=(width[0],width[1]), length=(length,length), fingers=(fingers[0],fingers[1]), multipliers=(multipliers[0],multipliers[1]), placement="vertical", with_dnwell=False) - bias_fvf_ref = prec_ref_center(bias_fvf) - bias_fvf_ref.movey(cascode_fvf_ref.ymin - 2 - (evaluate_bbox(bias_fvf)[1]/2)) - top_level.add(bias_fvf_ref) - - #creating fets for output branches - fet_1 = nmos(pdk, width=width[0], fingers=fingers[0], multipliers=multipliers[0], with_dummy=True, with_dnwell=False, with_substrate_tap=False, length=length) - fet_1_ref = prec_ref_center(fet_1) - fet_2_ref = prec_ref_center(fet_1) - fet_3_ref = prec_ref_center(fet_1) - fet_4_ref = prec_ref_center(fet_1) - - fet_1_ref.movex(cascode_fvf_ref.xmin - (evaluate_bbox(fet_1)[0]/2) - pdk.util_max_metal_seperation()) - fet_2_ref.movex(cascode_fvf_ref.xmin - (3*evaluate_bbox(fet_1)[0]/2) - 2*pdk.util_max_metal_seperation()) - fet_3_ref.movex(cascode_fvf_ref.xmax + (evaluate_bbox(fet_1)[0]/2) + pdk.util_max_metal_seperation()) - fet_4_ref.movex(cascode_fvf_ref.xmax + (3*evaluate_bbox(fet_1)[0]/2) + 2*pdk.util_max_metal_seperation()) - - top_level.add(fet_1_ref) - top_level.add(fet_2_ref) - top_level.add(fet_3_ref) - top_level.add(fet_4_ref) - - top_level << c_route(pdk, bias_fvf_ref.ports["A_multiplier_0_gate_E"], bias_fvf_ref.ports["B_gate_bottom_met_E"]) - top_level << c_route(pdk, cascode_fvf_ref.ports["A_multiplier_0_gate_W"], bias_fvf_ref.ports["A_multiplier_0_gate_W"]) - top_level << straight_route(pdk, cascode_fvf_ref.ports["B_gate_bottom_met_E"], fet_3_ref.ports["multiplier_0_gate_W"]) - - #creating vias for routing - viam2m3 = via_stack(pdk, "met2", "met3", centered=True) - gate_1_via = top_level << viam2m3 - gate_1_via.move(fet_1_ref.ports["multiplier_0_gate_W"].center).movex(-1) - gate_2_via = top_level << viam2m3 - gate_2_via.move(fet_2_ref.ports["multiplier_0_gate_W"].center).movex(-1) - gate_3_via = top_level << viam2m3 - gate_3_via.move(fet_3_ref.ports["multiplier_0_gate_E"].center).movex(1) - gate_4_via = top_level << viam2m3 - gate_4_via.move(fet_4_ref.ports["multiplier_0_gate_E"].center).movex(1) - - source_2_via = top_level << viam2m3 - drain_1_via = top_level << viam2m3 - source_2_via.move(fet_2_ref.ports["multiplier_0_source_E"].center).movex(1.5) - drain_1_via.move(fet_1_ref.ports["multiplier_0_drain_W"].center).movex(-1) - - source_4_via = top_level << viam2m3 - drain_3_via = top_level << viam2m3 - source_4_via.move(fet_4_ref.ports["multiplier_0_source_W"].center).movex(-1) - drain_3_via.move(fet_3_ref.ports["multiplier_0_drain_E"].center).movex(1.5) - - #routing - top_level << straight_route(pdk, fet_2_ref.ports["multiplier_0_source_E"], source_2_via.ports["bottom_met_W"]) - top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_drain_W"], drain_1_via.ports["bottom_met_E"]) - top_level << straight_route(pdk, fet_4_ref.ports["multiplier_0_source_W"], source_4_via.ports["bottom_met_E"]) - top_level << straight_route(pdk, fet_3_ref.ports["multiplier_0_drain_E"], drain_3_via.ports["bottom_met_W"]) - top_level << c_route(pdk, source_2_via.ports["top_met_N"], drain_1_via.ports["top_met_N"], extension=0.5*evaluate_bbox(fet_1)[1], width1=0.32, width2=0.32, cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met2") - top_level << c_route(pdk, source_4_via.ports["top_met_N"], drain_3_via.ports["top_met_N"], extension=0.5*evaluate_bbox(fet_1)[1], width1=0.32, width2=0.32, cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met2") - top_level << c_route(pdk, bias_fvf_ref.ports["A_multiplier_0_gate_E"], gate_4_via.ports["bottom_met_E"], width1=0.32, width2=0.32, cwidth=0.32) - - - top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_gate_W"], gate_1_via.ports["bottom_met_E"]) - top_level << straight_route(pdk, fet_2_ref.ports["multiplier_0_gate_W"], gate_2_via.ports["bottom_met_E"]) - top_level << straight_route(pdk, fet_3_ref.ports["multiplier_0_gate_E"], gate_3_via.ports["bottom_met_W"]) - top_level << straight_route(pdk, fet_4_ref.ports["multiplier_0_gate_E"], gate_4_via.ports["bottom_met_W"]) - - top_level << c_route(pdk, gate_1_via.ports["top_met_S"], gate_3_via.ports["top_met_S"], extension=(1.2*width[0]+0.6), cglayer='met2') - top_level << c_route(pdk, gate_2_via.ports["top_met_S"], gate_4_via.ports["top_met_S"], extension=(1.2*width[0]-0.6), cglayer='met2') - - top_level << straight_route(pdk, fet_1_ref.ports["multiplier_0_source_W"], fet_1_ref.ports["tie_W_top_met_W"], glayer1='met1', width=0.2) - top_level << straight_route(pdk, fet_3_ref.ports["multiplier_0_source_W"], fet_3_ref.ports["tie_W_top_met_W"], glayer1='met1', width=0.2) - - - top_level.add_ports(bias_fvf_ref.get_ports_list(), prefix="M_1_") - top_level.add_ports(cascode_fvf_ref.get_ports_list(), prefix="M_2_") - top_level.add_ports(fet_1_ref.get_ports_list(), prefix="M_3_B_") - top_level.add_ports(fet_2_ref.get_ports_list(), prefix="M_3_A_") - top_level.add_ports(fet_3_ref.get_ports_list(), prefix="M_4_B_") - top_level.add_ports(fet_4_ref.get_ports_list(), prefix="M_4_A_") - - component = component_snap_to_grid(rename_ports_by_orientation(top_level)) - component.info['netlist'] = low_voltage_cmirr_netlist(bias_fvf, cascode_fvf, fet_1_ref, fet_2_ref, fet_3_ref, fet_4_ref) - - return component - -if __name__=="__main__": - #low_voltage_current_mirror = low_voltage_current_mirror(sky130_mapped_pdk) - low_voltage_current_mirror = add_lvcm_labels(low_voltage_cmirror(sky130_mapped_pdk),sky130_mapped_pdk) - low_voltage_current_mirror.show() - low_voltage_current_mirror.name = "Low_voltage_current_mirror" - #magic_drc_result = sky130_mapped_pdk.drc_magic(low_voltage_current_mirror, low_voltage_current_mirror.name) - #netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(low_voltage_current_mirror, low_voltage_current_mirror.name) - low_voltage_current_mirror_gds = low_voltage_current_mirror.write_gds("low_voltage_current_mirror.gds") - res = run_evaluation("low_voltage_current_mirror.gds", low_voltage_current_mirror.name, low_voltage_current_mirror) \ No newline at end of file diff --git a/src/glayout/blocks/ATLAS/opamp.py b/src/glayout/blocks/ATLAS/opamp_wrapper.py similarity index 88% rename from src/glayout/blocks/ATLAS/opamp.py rename to src/glayout/blocks/ATLAS/opamp_wrapper.py index d5b25690..17b54962 100644 --- a/src/glayout/blocks/ATLAS/opamp.py +++ b/src/glayout/blocks/ATLAS/opamp_wrapper.py @@ -1,18 +1,18 @@ from gdsfactory.read.import_gds import import_gds from gdsfactory.components import text_freetype, rectangle -from glayout.flow.pdk.util.comp_utils import prec_array, movey, align_comp_to_port, prec_ref_center -from glayout.flow.pdk.util.port_utils import add_ports_perimeter, print_ports +from glayout.util.comp_utils import prec_array, movey, align_comp_to_port, prec_ref_center +from glayout.util.port_utils import add_ports_perimeter, print_ports from gdsfactory.component import Component -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.blocks.composite.opamp.opamp import opamp -from glayout.flow.routing.L_route import L_route -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.primitives.via_gen import via_array +from glayout.pdk.mappedpdk import MappedPDK +from glayout.blocks.composite.opamp.opamp import opamp +from glayout.routing.L_route import L_route +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.primitives.via_gen import via_array from gdsfactory.cell import cell, clear_cache -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as pdk -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.flow.pdk.util.component_array_create import write_component_matrix +from glayout.pdk.sky130_mapped import sky130_mapped_pdk as pdk +from glayout.util.snap_to_grid import component_snap_to_grid +from glayout.util.component_array_create import write_component_matrix from evaluator_wrapper import run_evaluation def sky130_add_opamp_2_labels(opamp_in: Component) -> Component: """adds opamp labels for extraction, without adding pads diff --git a/src/glayout/blocks/ATLAS/resume_fvf_nohup.py b/src/glayout/blocks/ATLAS/resume_fvf_nohup.py deleted file mode 100755 index a192ff49..00000000 --- a/src/glayout/blocks/ATLAS/resume_fvf_nohup.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -"""Resume the FVF generation non-interactively and exit with status. - -This script imports the updated generator and calls run_dataset_generation -directly. It's intended to be launched under nohup or a systemd service so it -continues after SSH disconnects. -""" -import logging -import sys - -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -logger = logging.getLogger(__name__) - -try: - from generate_fvf_8h_runtime_aware import load_fvf_parameters, run_dataset_generation -except Exception as e: - logger.error(f"Failed to import generator module: {e}") - sys.exit(2) - - -def main(): - try: - params = load_fvf_parameters(None) - n = len(params) - logger.info(f"Resuming generation for {n} samples (checkpoint-aware)") - - # Run dataset generation; it will load and resume from checkpoint.json - success, passed, total = run_dataset_generation(n, "fvf_dataset_8h_runtime_aware", checkpoint_interval=100, resume_from_checkpoint=True) - - logger.info(f"Finished. success={success}, passed={passed}, total={total}") - return 0 if success else 1 - except Exception as e: - logger.exception(f"Unexpected error during resume: {e}") - return 3 - - -if __name__ == '__main__': - rc = main() - sys.exit(rc) diff --git a/src/glayout/blocks/ATLAS/robust_verification.py b/src/glayout/blocks/ATLAS/robust_verification.py index ea309be8..f90049af 100644 --- a/src/glayout/blocks/ATLAS/robust_verification.py +++ b/src/glayout/blocks/ATLAS/robust_verification.py @@ -272,24 +272,27 @@ def run_robust_verification(layout_path: str, component_name: str, top_level: Co # Import sky130_mapped_pdk *after* the environment is guaranteed sane so # that gdsfactory/PDK initialization picks up the correct PDK_ROOT. - from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk + from glayout.pdk.sky130_mapped import sky130_mapped_pdk # DRC Check - drc_report_path = os.path.abspath(f"./{component_name}.drc.rpt") + # drc_magic expects a directory and creates: {output_dir}/drc/{design_name}/{design_name}.rpt + drc_output_dir = os.path.abspath(".") + drc_report_path = os.path.join(drc_output_dir, "drc", component_name, f"{component_name}.rpt") verification_results["drc"]["report_path"] = drc_report_path try: - # Clean up any existing DRC report - if os.path.exists(drc_report_path): - os.remove(drc_report_path) + # Clean up any existing DRC report directory + drc_dir = os.path.join(drc_output_dir, "drc", component_name) + if os.path.exists(drc_dir): + shutil.rmtree(drc_dir) # Ensure PDK environment again right before DRC ensure_pdk_environment() print(f"Running DRC for {component_name}...") - # Try the PDK DRC method first - sky130_mapped_pdk.drc_magic(layout_path, component_name, output_file=drc_report_path) + # Pass directory as output_file, not the file path + sky130_mapped_pdk.drc_magic(layout_path, component_name, output_file=drc_output_dir) # Check if report was created and read it report_content = "" @@ -297,13 +300,9 @@ def run_robust_verification(layout_path: str, component_name: str, top_level: Co with open(drc_report_path, 'r') as f: report_content = f.read() print(f"DRC report created successfully: {len(report_content)} chars") - '''else: - print("Warning: DRC report file was not created, creating empty report") - # Create empty report as fallback - report_content = f"{component_name} count: \n----------------------------------------\n\n" - with open(drc_report_path, 'w') as f: - f.write(report_content) - ''' + else: + print(f"Warning: DRC report not found at {drc_report_path}") + summary = parse_drc_report(report_content) verification_results["drc"].update({ "summary": summary, @@ -313,35 +312,31 @@ def run_robust_verification(layout_path: str, component_name: str, top_level: Co except Exception as e: print(f"DRC failed with exception: {e}") - # Create a basic report even on failure - try: - with open(drc_report_path, 'w') as f: - f.write(f"DRC Error for {component_name}\n") - f.write(f"Error: {str(e)}\n") - verification_results["drc"]["status"] = f"error: {e}" - except: - verification_results["drc"]["status"] = f"error: {e}" + verification_results["drc"]["status"] = f"error: {e}" # Small delay between DRC and LVS import time time.sleep(1) # LVS Check - lvs_report_path = os.path.abspath(f"./{component_name}.lvs.rpt") + # lvs_netgen expects a directory and creates: {output_dir}/lvs/{design_name}/{design_name}_lvs.rpt + lvs_output_dir = os.path.abspath(".") + lvs_report_path = os.path.join(lvs_output_dir, "lvs", component_name, f"{component_name}_lvs.rpt") verification_results["lvs"]["report_path"] = lvs_report_path try: - # Clean up any existing LVS report - if os.path.exists(lvs_report_path): - os.remove(lvs_report_path) + # Clean up any existing LVS report directory + lvs_dir = os.path.join(lvs_output_dir, "lvs", component_name) + if os.path.exists(lvs_dir): + shutil.rmtree(lvs_dir) # Ensure PDK environment again right before LVS ensure_pdk_environment() print(f"Running LVS for {component_name}...") - # Try the PDK LVS method first - sky130_mapped_pdk.lvs_netgen(layout=top_level, design_name=component_name, output_file_path=lvs_report_path) + # Pass directory as output_file_path, not the file path + sky130_mapped_pdk.lvs_netgen(layout=top_level, design_name=component_name, output_file_path=lvs_output_dir) # Check if report was created and read it report_content = "" @@ -349,13 +344,9 @@ def run_robust_verification(layout_path: str, component_name: str, top_level: Co with open(lvs_report_path, 'r') as report_file: report_content = report_file.read() print(f"LVS report created successfully: {len(report_content)} chars") - '''else: - print("Warning: LVS report file was not created, creating fallback report") - # Create fallback report - report_content = f"LVS Report for {component_name}\nFinal result: Circuits match uniquely.\nLVS Done.\n" - with open(lvs_report_path, 'w') as f: - f.write(report_content) - ''' + else: + print(f"Warning: LVS report not found at {lvs_report_path}") + lvs_summary = parse_lvs_report(report_content) verification_results["lvs"].update({ "summary": lvs_summary, @@ -365,14 +356,7 @@ def run_robust_verification(layout_path: str, component_name: str, top_level: Co except Exception as e: print(f"LVS failed with exception: {e}") - # Create a basic report even on failure - try: - with open(lvs_report_path, 'w') as f: - f.write(f"LVS Error for {component_name}\n") - f.write(f"Error: {str(e)}\n") - verification_results["lvs"]["status"] = f"error: {e}" - except: - verification_results["lvs"]["status"] = f"error: {e}" + verification_results["lvs"]["status"] = f"error: {e}" # Small delay between LVS and PEX time.sleep(1) diff --git a/src/glayout/blocks/ATLAS/run_lhs_files.py b/src/glayout/blocks/ATLAS/run_lhs_files.py deleted file mode 100644 index 4a81cb2b..00000000 --- a/src/glayout/blocks/ATLAS/run_lhs_files.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -"""Run and time LHS generator files once and emit a JSON array of results. - -This script will attempt to execute the following files (located in the same -directory) once each and measure wall-clock time for the run: - -- current_mirror.py -- diff_pair.py -- fvf.py -- transmission_gate.py -- lvcm.py - -It records start/stop times, exit codes, elapsed seconds and any stderr output -into a JSON file named `run_lhs_results.json` and prints the JSON array to -stdout. -""" -import json -import os -import sys -import time -import subprocess - - -FILES = [ - "current_mirror.py", - "diff_pair.py", - "fvf.py", - "transmission_gate.py", - "lvcm.py", -] - - -def run_file(path, timeout=120): - """Run a python file and time the execution. Returns a dict with results.""" - start = time.perf_counter() - try: - completed = subprocess.run([sys.executable, path], capture_output=True, text=True, timeout=timeout) - end = time.perf_counter() - return { - "file": os.path.basename(path), - "elapsed_seconds": end - start, - "returncode": completed.returncode, - "stdout": completed.stdout.strip(), - "stderr": completed.stderr.strip(), - } - except subprocess.TimeoutExpired as e: - end = time.perf_counter() - return { - "file": os.path.basename(path), - "elapsed_seconds": end - start, - "returncode": None, - "stdout": "", - "stderr": f"Timeout after {timeout}s", - } - except Exception as e: - end = time.perf_counter() - return { - "file": os.path.basename(path), - "elapsed_seconds": end - start, - "returncode": None, - "stdout": "", - "stderr": f"Exception: {e}", - } - - -def main(): - base = os.path.dirname(os.path.abspath(__file__)) - results = [] - for fname in FILES: - fpath = os.path.join(base, fname) - if not os.path.exists(fpath): - results.append({ - "file": fname, - "elapsed_seconds": None, - "returncode": None, - "stdout": "", - "stderr": "File not found", - }) - continue - print(f"Running {fname}...") - res = run_file(fpath) - print(f" -> {fname}: {res['elapsed_seconds']:.4f}s, returncode={res['returncode']}") - results.append(res) - - out_path = os.path.join(base, "run_lhs_results.json") - with open(out_path, "w") as f: - json.dump(results, f, indent=2) - - # Print only the array of elapsed_seconds for quick consumption, then full JSON - elapsed_array = [r["elapsed_seconds"] for r in results] - print("\nElapsed seconds array:") - print(json.dumps(elapsed_array)) - print("\nFull results saved to:", out_path) - print(json.dumps(results, indent=2)) - - -if __name__ == "__main__": - main() diff --git a/src/glayout/blocks/ATLAS/test_comprehensive_fix.py b/src/glayout/blocks/ATLAS/test_comprehensive_fix.py deleted file mode 100644 index 76da9854..00000000 --- a/src/glayout/blocks/ATLAS/test_comprehensive_fix.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 -""" -Comprehensive test script to verify that all netlist info dict fixes work correctly. -Tests multiple components to ensure the fix is applied consistently. -""" - -import sys -import os -import json -from pathlib import Path - -# Add the glayout path -glayout_path = "/home/arnavshukla/OpenFASOC/openfasoc/generators/glayout" -if glayout_path not in sys.path: - sys.path.insert(0, glayout_path) - -# Set up environment -os.environ['PDK_ROOT'] = '/opt/conda/envs/GLdev/share/pdk' -os.environ['PDK'] = 'sky130A' - -def test_component_info_serialization(component, component_name): - """Test that a component's info dict can be JSON serialized""" - print(f"\nTesting {component_name}...") - - try: - # Check netlist storage - netlist_value = component.info.get('netlist') - netlist_data = component.info.get('netlist_data') - - print(f" Netlist type: {type(netlist_value)}") - print(f" Netlist data type: {type(netlist_data)}") - - success = True - - # Verify netlist is stored as string - if not isinstance(netlist_value, str): - print(f" āŒ FAILED: netlist should be string, got {type(netlist_value)}") - success = False - else: - print(" āœ… SUCCESS: netlist is stored as string") - - # Verify netlist_data is available for gdsfactory 7.16.0+ compatibility - if netlist_data is None: - print(" āš ļø WARNING: netlist_data is None - may not work with gdsfactory 7.16.0+") - elif isinstance(netlist_data, dict): - required_keys = ['circuit_name', 'nodes', 'source_netlist'] - if all(key in netlist_data for key in required_keys): - print(" āœ… SUCCESS: netlist_data contains all required fields for reconstruction") - else: - print(f" āŒ FAILED: netlist_data missing required keys: {[k for k in required_keys if k not in netlist_data]}") - success = False - else: - print(f" āŒ FAILED: netlist_data should be dict, got {type(netlist_data)}") - success = False - - # Test JSON serialization - try: - info_copy = {} - for key, value in component.info.items(): - if isinstance(value, (str, int, float, bool, list, tuple, dict)): - info_copy[key] = value - else: - info_copy[key] = str(value) - - json_str = json.dumps(info_copy, indent=2) - print(" āœ… SUCCESS: info dict can be JSON serialized") - - except Exception as e: - print(f" āŒ FAILED: JSON serialization failed: {e}") - success = False - - return success - - except Exception as e: - print(f" āŒ FAILED: Test failed with error: {e}") - return False - -def main(): - """Test multiple components to ensure consistent behavior""" - print("🧪 Comprehensive Netlist Serialization Test") - print("=" * 60) - - from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk - pdk = sky130_mapped_pdk - - test_results = [] - - # Test 1: Basic FETs - try: - print("\nšŸ“‹ Testing Basic Components...") - from glayout.flow.primitives.fet import nmos, pmos - - nfet = nmos(pdk, width=1.0, length=0.15, fingers=1) - test_results.append(("NMOS", test_component_info_serialization(nfet, "NMOS"))) - - pfet = pmos(pdk, width=2.0, length=0.15, fingers=1) - test_results.append(("PMOS", test_component_info_serialization(pfet, "PMOS"))) - - except Exception as e: - print(f"āŒ Failed to test basic FETs: {e}") - test_results.append(("Basic FETs", False)) - - # Test 2: Transmission Gate - try: - print("\nšŸ“‹ Testing Transmission Gate...") - from transmission_gate import transmission_gate - - tg = transmission_gate( - pdk=pdk, - width=(1.0, 2.0), - length=(0.15, 0.15), - fingers=(1, 1), - multipliers=(1, 1) - ) - test_results.append(("Transmission Gate", test_component_info_serialization(tg, "Transmission Gate"))) - - except Exception as e: - print(f"āŒ Failed to test transmission gate: {e}") - test_results.append(("Transmission Gate", False)) - - # Test 3: FVF (if available) - try: - print("\nšŸ“‹ Testing Flipped Voltage Follower...") - from fvf import flipped_voltage_follower - - fvf = flipped_voltage_follower( - pdk=pdk, - width=(1.0, 0.5), - length=(0.15, 0.15), - fingers=(1, 1) - ) - test_results.append(("FVF", test_component_info_serialization(fvf, "Flipped Voltage Follower"))) - - except Exception as e: - print(f"āš ļø FVF test skipped: {e}") - - # Test 4: MIM Capacitor (if available) - try: - print("\nšŸ“‹ Testing MIM Capacitor...") - from glayout.flow.primitives.mimcap import mimcap - - cap = mimcap(pdk=pdk, size=(5.0, 5.0)) - test_results.append(("MIM Cap", test_component_info_serialization(cap, "MIM Capacitor"))) - - except Exception as e: - print(f"āš ļø MIM Cap test skipped: {e}") - - # Summary - print("\n" + "=" * 60) - print("šŸ“Š TEST SUMMARY") - print("=" * 60) - - passed = sum(1 for _, result in test_results if result) - total = len(test_results) - - for component_name, result in test_results: - status = "āœ… PASS" if result else "āŒ FAIL" - print(f"{status}: {component_name}") - - print(f"\nOverall: {passed}/{total} tests passed ({passed/total*100:.1f}%)") - - if passed == total: - print("\nšŸŽ‰ ALL TESTS PASSED!") - print("The gymnasium info dict error should be resolved for your friend.") - print("\nSolution Summary:") - print("- All netlist objects are now stored as strings in component.info['netlist']") - print("- Netlist data is preserved in component.info['netlist_data'] for reconstruction") - print("- This prevents gymnasium from encountering unsupported object types") - print("- Compatible with both gdsfactory 7.7.0 and 7.16.0+ strict Pydantic validation") - return True - else: - print(f"\nāš ļø {total - passed} tests failed. Some issues may remain.") - return False - -if __name__ == "__main__": - success = main() - if success: - print("\nāœ… Fix validation completed successfully!") - else: - print("\nāŒ Some issues detected. Please review the failed tests.") diff --git a/src/glayout/blocks/ATLAS/test_lvs_fix.py b/src/glayout/blocks/ATLAS/test_lvs_fix.py deleted file mode 100644 index 1fce7709..00000000 --- a/src/glayout/blocks/ATLAS/test_lvs_fix.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script to verify LVS functionality works with the netlist serialization fix. -Tests specifically for the 'str' object has no attribute 'generate_netlist' error. -""" - -import sys -import os -from pathlib import Path - -# Add the glayout path -glayout_path = "/home/arnavshukla/OpenFASOC/openfasoc/generators/glayout" -if glayout_path not in sys.path: - sys.path.insert(0, glayout_path) - -# Set up environment -os.environ['PDK_ROOT'] = '/opt/conda/envs/GLdev/share/pdk' -os.environ['PDK'] = 'sky130A' - -def test_lvs_netlist_generation(): - """Test that LVS can generate netlists from component info without errors""" - print("🧪 Testing LVS Netlist Generation Fix...") - - try: - from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk - from transmission_gate import transmission_gate, add_tg_labels - - pdk = sky130_mapped_pdk - - print("šŸ“‹ Creating transmission gate component...") - tg = transmission_gate( - pdk=pdk, - width=(1.0, 2.0), - length=(0.15, 0.15), - fingers=(1, 1), - multipliers=(1, 1) - ) - - print("šŸ“‹ Adding labels...") - tg_labeled = add_tg_labels(tg, pdk) - tg_labeled.name = "test_transmission_gate" - - print("šŸ“‹ Testing netlist generation in LVS context...") - - # Test the netlist generation logic from mappedpdk.py - from glayout.flow.spice.netlist import Netlist - - # Simulate what happens in lvs_netgen when netlist is None - layout = tg_labeled - - # Try to get stored object first (for older gdsfactory versions) - if 'netlist_obj' in layout.info: - print("āœ… Found netlist_obj in component.info") - netlist_obj = layout.info['netlist_obj'] - # Try to reconstruct from netlist_data (for newer gdsfactory versions) - elif 'netlist_data' in layout.info: - print("āœ… Found netlist_data in component.info") - data = layout.info['netlist_data'] - netlist_obj = Netlist( - circuit_name=data['circuit_name'], - nodes=data['nodes'] - ) - netlist_obj.source_netlist = data['source_netlist'] - else: - # Fallback: if it's already a string, use it directly - print("ā„¹ļø Using string fallback for netlist") - netlist_string = layout.info.get('netlist', '') - if not isinstance(netlist_string, str): - print("āŒ FAILED: Expected string fallback but got:", type(netlist_string)) - return False - netlist_obj = None - - # Generate netlist if we have a netlist object - if netlist_obj is not None: - print("šŸ“‹ Testing generate_netlist() call...") - try: - netlist_content = netlist_obj.generate_netlist() - print("āœ… SUCCESS: generate_netlist() worked without error") - print(f"šŸ“„ Generated netlist length: {len(netlist_content)} characters") - - # Verify it contains expected content - if 'Transmission_Gate' in netlist_content: - print("āœ… SUCCESS: Netlist contains expected circuit name") - else: - print("āš ļø WARNING: Netlist doesn't contain expected circuit name") - - return True - - except AttributeError as e: - if "'str' object has no attribute 'generate_netlist'" in str(e): - print("āŒ FAILED: Still getting the 'str' object error:", e) - return False - else: - print("āŒ FAILED: Unexpected AttributeError:", e) - return False - except Exception as e: - print("āŒ FAILED: Unexpected error during generate_netlist():", e) - return False - else: - print("ā„¹ļø No netlist object to test - using string representation") - netlist_string = layout.info.get('netlist', '') - if isinstance(netlist_string, str) and len(netlist_string) > 0: - print("āœ… SUCCESS: String netlist available as fallback") - return True - else: - print("āŒ FAILED: No valid netlist representation found") - return False - - except Exception as e: - print(f"āŒ FAILED: Test failed with error: {e}") - import traceback - traceback.print_exc() - return False - -def test_actual_lvs_call(): - """Test a simplified LVS call to see if it works""" - print("\n🧪 Testing Actual LVS Functionality...") - - try: - from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk - from transmission_gate import transmission_gate, add_tg_labels - - pdk = sky130_mapped_pdk - - print("šŸ“‹ Creating and labeling transmission gate...") - tg = transmission_gate(pdk=pdk, width=(1.0, 2.0), length=(0.15, 0.15)) - tg_labeled = add_tg_labels(tg, pdk) - tg_labeled.name = "lvs_test_tg" - - print("šŸ“‹ Writing GDS file...") - gds_file = "lvs_test_tg.gds" - tg_labeled.write_gds(gds_file) - - print("šŸ“‹ Attempting LVS call...") - try: - # This should not fail with the "'str' object has no attribute 'generate_netlist'" error - result = pdk.lvs_netgen(tg_labeled, "lvs_test_tg") - print("āœ… SUCCESS: LVS call completed without netlist generation error") - print("šŸ“Š LVS result keys:", list(result.keys()) if isinstance(result, dict) else "Not a dict") - return True - - except AttributeError as e: - if "'str' object has no attribute 'generate_netlist'" in str(e): - print("āŒ FAILED: LVS still has the 'str' object error:", e) - return False - else: - print("āš ļø LVS failed with different AttributeError (may be expected):", e) - return True # The specific error we're fixing is resolved - - except Exception as e: - print("āš ļø LVS failed with other error (may be expected in test environment):", e) - print("ā„¹ļø This is likely due to missing PDK files or tools, not our fix") - return True # The specific error we're fixing is resolved - - except Exception as e: - print(f"āŒ FAILED: Test failed with error: {e}") - import traceback - traceback.print_exc() - return False - -def main(): - """Main test function""" - print("šŸ”§ Testing LVS Netlist Generation Fix") - print("=" * 50) - - test1_passed = test_lvs_netlist_generation() - test2_passed = test_actual_lvs_call() - - print("\n" + "=" * 50) - print("šŸ“Š TEST SUMMARY") - print("=" * 50) - - if test1_passed: - print("āœ… PASS: Netlist generation logic") - else: - print("āŒ FAIL: Netlist generation logic") - - if test2_passed: - print("āœ… PASS: LVS call functionality") - else: - print("āŒ FAIL: LVS call functionality") - - overall_success = test1_passed and test2_passed - - if overall_success: - print("\nšŸŽ‰ ALL TESTS PASSED!") - print("The 'str' object has no attribute 'generate_netlist' error should be resolved.") - return True - else: - print("\nāš ļø Some tests failed. The LVS fix may need further adjustment.") - return False - -if __name__ == "__main__": - success = main() - if success: - print("\nāœ… LVS fix validation completed successfully!") - else: - print("\nāŒ LVS fix validation failed.") diff --git a/src/glayout/blocks/ATLAS/test_netlist_fix.py b/src/glayout/blocks/ATLAS/test_netlist_fix.py deleted file mode 100644 index d49cfbbb..00000000 --- a/src/glayout/blocks/ATLAS/test_netlist_fix.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python3 -""" -Test script to verify that the netlist info dict fix works correctly. -""" - -import sys -import os -from pathlib import Path - -# Add the glayout path -glayout_path = "/home/arnavshukla/OpenFASOC/openfasoc/generators/glayout" -if glayout_path not in sys.path: - sys.path.insert(0, glayout_path) - -# Set up environment -os.environ['PDK_ROOT'] = '/opt/conda/envs/GLdev/share/pdk' -os.environ['PDK'] = 'sky130A' - -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk -from transmission_gate import transmission_gate, add_tg_labels - -def test_netlist_serialization(): - """Test that netlist objects are properly serialized in component.info""" - print("Testing transmission gate netlist serialization...") - - try: - # Create a transmission gate with default parameters - tg = transmission_gate( - pdk=sky130_mapped_pdk, - width=(1.0, 2.0), - length=(0.15, 0.15), - fingers=(1, 1), - multipliers=(1, 1) - ) - - # Check that netlist is stored as string (not object) - netlist_value = tg.info.get('netlist') - netlist_obj = tg.info.get('netlist_obj') - - print(f"Netlist type: {type(netlist_value)}") - print(f"Netlist object type: {type(netlist_obj)}") - - # Verify types - if isinstance(netlist_value, str): - print("āœ… SUCCESS: netlist is stored as string") - else: - print(f"āŒ FAILED: netlist is stored as {type(netlist_value)}") - return False - - if netlist_obj is not None and hasattr(netlist_obj, 'circuit_name'): - print("āœ… SUCCESS: netlist_obj is available for internal use") - else: - print("āŒ FAILED: netlist_obj is not properly stored") - return False - - # Test that we can create JSON-serializable info dict - import json - try: - # Create a copy of info dict with only basic types - info_copy = {} - for key, value in tg.info.items(): - if isinstance(value, (str, int, float, bool, list, tuple)): - info_copy[key] = value - else: - info_copy[key] = str(value) - - json_str = json.dumps(info_copy, indent=2) - print("āœ… SUCCESS: info dict can be JSON serialized") - print(f"JSON preview: {json_str[:200]}...") - - except Exception as e: - print(f"āŒ FAILED: JSON serialization failed: {e}") - return False - - return True - - except Exception as e: - print(f"āŒ FAILED: Test failed with error: {e}") - return False - -if __name__ == "__main__": - print("Testing netlist serialization fix...") - success = test_netlist_serialization() - if success: - print("\nšŸŽ‰ All tests passed! The fix should resolve the gymnasium info dict error.") - else: - print("\nāš ļø Tests failed. The issue may not be fully resolved.") diff --git a/src/glayout/blocks/ATLAS/transmission_gate.py b/src/glayout/blocks/ATLAS/transmission_gate.py deleted file mode 100644 index 3e42e7dc..00000000 --- a/src/glayout/blocks/ATLAS/transmission_gate.py +++ /dev/null @@ -1,182 +0,0 @@ -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk -from gdsfactory.cell import cell -from gdsfactory.component import Component -from gdsfactory import Component -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, align_comp_to_port, movex, movey -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.primitives.guardring import tapring -from glayout.flow.pdk.util.port_utils import add_ports_perimeter -from glayout.flow.spice.netlist import Netlist -from glayout.flow.primitives.via_gen import via_stack -from gdsfactory.components import text_freetype, rectangle -try: - from evaluator_wrapper import run_evaluation # pyright: ignore[reportMissingImports] -except ImportError: - print("Warning: evaluator_wrapper not found. Evaluation will be skipped.") - run_evaluation = None - -def add_tg_labels(tg_in: Component, - pdk: MappedPDK - ) -> Component: - - tg_in.unlock() - met2_pin = (68,16) - met2_label = (68,5) - # list that will contain all port/comp info - move_info = list() - # create labels and append to info list - # vin - vinlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vinlabel.add_label(text="VIN",layer=pdk.get_glayer("met2_label")) - move_info.append((vinlabel,tg_in.ports["N_multiplier_0_source_E"],None)) - - # vout - voutlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - voutlabel.add_label(text="VOUT",layer=pdk.get_glayer("met2_label")) - move_info.append((voutlabel,tg_in.ports["P_multiplier_0_drain_W"],None)) - - # vcc - vcclabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.5,0.5),centered=True).copy() - vcclabel.add_label(text="VCC",layer=pdk.get_glayer("met2_label")) - move_info.append((vcclabel,tg_in.ports["P_tie_S_top_met_S"],None)) - - # vss - vsslabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.5,0.5),centered=True).copy() - vsslabel.add_label(text="VSS",layer=pdk.get_glayer("met2_label")) - move_info.append((vsslabel,tg_in.ports["N_tie_S_top_met_N"], None)) - - # VGP - vgplabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vgplabel.add_label(text="VGP",layer=pdk.get_glayer("met2_label")) - move_info.append((vgplabel,tg_in.ports["P_multiplier_0_gate_E"], None)) - - # VGN - vgnlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() - vgnlabel.add_label(text="VGN",layer=pdk.get_glayer("met2_label")) - move_info.append((vgnlabel,tg_in.ports["N_multiplier_0_gate_E"], None)) - - # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - compref = align_comp_to_port(comp, prt, alignment=alignment) - tg_in.add(compref) - return tg_in.flatten() - - -def get_component_netlist(component) -> Netlist: - """Helper function to extract netlist from component with version compatibility""" - if hasattr(component.info, 'get'): - # Check if netlist object is stored directly - if 'netlist' in component.info: - netlist_obj = component.info['netlist'] - if isinstance(netlist_obj, str): - # It's a string representation, try to reconstruct - # For gymnasium compatibility, we don't store netlist_data, so create a simple netlist - return Netlist(source_netlist=netlist_obj) - else: - # It's already a Netlist object - return netlist_obj - - # Fallback: return empty netlist - return Netlist() - -def tg_netlist(nfet_comp, pfet_comp) -> str: - """Generate SPICE netlist string for transmission gate - gymnasium compatible""" - - # Get the SPICE netlists directly from components - nmos_spice = nfet_comp.info.get('netlist', '') - pmos_spice = pfet_comp.info.get('netlist', '') - - if not nmos_spice or not pmos_spice: - raise ValueError("Component netlists not found") - - # Create the transmission gate SPICE netlist by combining the primitives - tg_spice = f"""{nmos_spice} - -{pmos_spice} - -.subckt transmission_gate D G S VDD VSS -* PMOS: connects D to S when G is low (G_n is high) -X0 D G_n S VDD PMOS -* NMOS: connects D to S when G is high -X1 D G S VSS NMOS -.ends transmission_gate -""" - - return tg_spice - -@cell -def transmission_gate( - pdk: MappedPDK, - width: tuple[float,float] = (1,1), - length: tuple[float,float] = (None,None), - fingers: tuple[int,int] = (1,1), - multipliers: tuple[int,int] = (1,1), - substrate_tap: bool = False, - tie_layers: tuple[str,str] = ("met2","met1"), - **kwargs - ) -> Component: - """ - creates a transmission gate - tuples are in (NMOS,PMOS) order - **kwargs are any kwarg that is supported by nmos and pmos - """ - - #top level component - top_level = Component(name="transmission_gate") - - #two fets - nfet = nmos(pdk, width=width[0], fingers=fingers[0], multipliers=multipliers[0], with_dummy=True, with_dnwell=False, with_substrate_tap=False, length=length[0], **kwargs) - pfet = pmos(pdk, width=width[1], fingers=fingers[1], multipliers=multipliers[1], with_dummy=True, with_substrate_tap=False, length=length[1], **kwargs) - nfet_ref = top_level << nfet - pfet_ref = top_level << pfet - pfet_ref = rename_ports_by_orientation(pfet_ref.mirror_y()) - - #Relative move - pfet_ref.movey(nfet_ref.ymax + evaluate_bbox(pfet_ref)[1]/2 + pdk.util_max_metal_seperation()) - - #Routing - top_level << c_route(pdk, nfet_ref.ports["multiplier_0_source_E"], pfet_ref.ports["multiplier_0_source_E"]) - top_level << c_route(pdk, nfet_ref.ports["multiplier_0_drain_W"], pfet_ref.ports["multiplier_0_drain_W"], viaoffset=False) - - #Renaming Ports - top_level.add_ports(nfet_ref.get_ports_list(), prefix="N_") - top_level.add_ports(pfet_ref.get_ports_list(), prefix="P_") - - #substrate tap - if substrate_tap: - substrate_tap_encloses =((evaluate_bbox(top_level)[0]+pdk.util_max_metal_seperation()), (evaluate_bbox(top_level)[1]+pdk.util_max_metal_seperation())) - guardring_ref = top_level << tapring( - pdk, - enclosed_rectangle=substrate_tap_encloses, - sdlayer="p+s/d", - horizontal_glayer='met2', - vertical_glayer='met1', - ) - guardring_ref.move(nfet_ref.center).movey(evaluate_bbox(pfet_ref)[1]/2 + pdk.util_max_metal_seperation()/2) - top_level.add_ports(guardring_ref.get_ports_list(),prefix="tap_") - - component = component_snap_to_grid(rename_ports_by_orientation(top_level)) - # Generate netlist as SPICE string for gymnasium compatibility - netlist_string = tg_netlist(nfet, pfet) - - # Store as string for gymnasium compatibility - LVS method supports this directly - component.info['netlist'] = netlist_string - - - return component - -if __name__=="__main__": - transmission_gate = add_tg_labels(transmission_gate(sky130_mapped_pdk),sky130_mapped_pdk) - transmission_gate.show() - transmission_gate.name = "Transmission_Gate" - #magic_drc_result = sky130_mapped_pdk.drc_magic(transmission_gate, transmission_gate.name) - #netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(transmission_gate, transmission_gate.name) - transmission_gate_gds = transmission_gate.write_gds("transmission_gate.gds") - res = run_evaluation("transmission_gate.gds", transmission_gate.name, transmission_gate) \ No newline at end of file diff --git a/src/glayout/blocks/composite/fvf_based_ota/low_voltage_cmirror.py b/src/glayout/blocks/composite/fvf_based_ota/low_voltage_cmirror.py index 61f8ff8a..5a6b5122 100644 --- a/src/glayout/blocks/composite/fvf_based_ota/low_voltage_cmirror.py +++ b/src/glayout/blocks/composite/fvf_based_ota/low_voltage_cmirror.py @@ -1,22 +1,22 @@ -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk +from glayout.pdk.mappedpdk import MappedPDK +from glayout.pdk.sky130_mapped import sky130_mapped_pdk from gdsfactory.component import Component from gdsfactory.component_reference import ComponentReference from gdsfactory.cell import cell from gdsfactory import Component from gdsfactory.components import text_freetype, rectangle -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, align_comp_to_port, prec_ref_center -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.primitives.guardring import tapring -from glayout.flow.pdk.util.port_utils import add_ports_perimeter -from glayout.flow.spice.netlist import Netlist -from glayout.flow.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower -from glayout.flow.primitives.via_gen import via_stack +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.util.comp_utils import evaluate_bbox, prec_center, align_comp_to_port, prec_ref_center +from glayout.util.snap_to_grid import component_snap_to_grid +from glayout.util.port_utils import rename_ports_by_orientation +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route +from glayout.primitives.guardring import tapring +from glayout.util.port_utils import add_ports_perimeter +from glayout.spice.netlist import Netlist +from glayout.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower +from glayout.primitives.via_gen import via_stack from typing import Optional def add_lvcm_labels(lvcm_in: Component, diff --git a/src/glayout/blocks/composite/fvf_based_ota/n_block.py b/src/glayout/blocks/composite/fvf_based_ota/n_block.py index 0a0ebdbb..af02e0e7 100644 --- a/src/glayout/blocks/composite/fvf_based_ota/n_block.py +++ b/src/glayout/blocks/composite/fvf_based_ota/n_block.py @@ -1,23 +1,23 @@ -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk +from glayout.pdk.mappedpdk import MappedPDK +from glayout.pdk.sky130_mapped import sky130_mapped_pdk from gdsfactory import Component from gdsfactory.cell import cell from gdsfactory.component_reference import ComponentReference -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_ref_center, prec_center, align_comp_to_port -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.util.comp_utils import evaluate_bbox, prec_ref_center, prec_center, align_comp_to_port +from glayout.util.port_utils import rename_ports_by_orientation +from glayout.util.snap_to_grid import component_snap_to_grid from gdsfactory.components import text_freetype, rectangle -from glayout.flow.spice.netlist import Netlist -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower -from glayout.flow.blocks.elementary.current_mirror.current_mirror import current_mirror, current_mirror_netlist -from glayout.flow.primitives.via_gen import via_stack, via_array -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.blocks.composite.fvf_based_ota.low_voltage_cmirror import low_voltage_cmirror, low_voltage_cmirr_netlist +from glayout.spice.netlist import Netlist +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route +from glayout.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower +from glayout.blocks.elementary.current_mirror.current_mirror import current_mirror, current_mirror_netlist +from glayout.primitives.via_gen import via_stack, via_array +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.blocks.composite.fvf_based_ota.low_voltage_cmirror import low_voltage_cmirror, low_voltage_cmirr_netlist def n_block_netlist(fet_inA_ref: ComponentReference, fet_inB_ref: ComponentReference, fvf_1_ref: ComponentReference, fvf_2_ref: ComponentReference, cmirror: Component, global_c_bias: Component) -> Netlist: diff --git a/src/glayout/blocks/composite/fvf_based_ota/ota.py b/src/glayout/blocks/composite/fvf_based_ota/ota.py index 87bcacc9..b8ffba78 100644 --- a/src/glayout/blocks/composite/fvf_based_ota/ota.py +++ b/src/glayout/blocks/composite/fvf_based_ota/ota.py @@ -1,26 +1,26 @@ -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk +from glayout.pdk.mappedpdk import MappedPDK +from glayout.pdk.sky130_mapped import sky130_mapped_pdk from gdsfactory import Component from gdsfactory.cell import cell from gdsfactory.component_reference import ComponentReference -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_ref_center, prec_center, align_comp_to_port -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.util.comp_utils import evaluate_bbox, prec_ref_center, prec_center, align_comp_to_port +from glayout.util.port_utils import rename_ports_by_orientation +from glayout.util.snap_to_grid import component_snap_to_grid from gdsfactory.components import text_freetype, rectangle -from glayout.flow.spice.netlist import Netlist +from glayout.spice.netlist import Netlist -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower -from glayout.flow.blocks.elementary.current_mirror.current_mirror import current_mirror, current_mirror_netlist -from glayout.flow.primitives.via_gen import via_stack, via_array -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.blocks.elementary.transmission_gate.transmission_gate import transmission_gate,tg_netlist -from glayout.flow.blocks.composite.fvf_based_ota.p_block import p_block,p_block_netlist -from glayout.flow.blocks.composite.fvf_based_ota.n_block import n_block,n_block_netlist +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route +from glayout.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower +from glayout.blocks.elementary.current_mirror.current_mirror import current_mirror, current_mirror_netlist +from glayout.primitives.via_gen import via_stack, via_array +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.blocks.elementary.transmission_gate.transmission_gate import transmission_gate,tg_netlist +from glayout.blocks.composite.fvf_based_ota.p_block import p_block,p_block_netlist +from glayout.blocks.composite.fvf_based_ota.n_block import n_block,n_block_netlist def super_class_AB_OTA_netlist(local_c_bias_1_ref: ComponentReference, local_c_bias_2_ref: ComponentReference, res_1_ref: ComponentReference, res_2_ref: ComponentReference, nb: Component, pblock: Component) -> Netlist: diff --git a/src/glayout/blocks/composite/fvf_based_ota/p_block.py b/src/glayout/blocks/composite/fvf_based_ota/p_block.py index 17e4f05c..db67187e 100644 --- a/src/glayout/blocks/composite/fvf_based_ota/p_block.py +++ b/src/glayout/blocks/composite/fvf_based_ota/p_block.py @@ -1,22 +1,22 @@ -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk +from glayout.pdk.mappedpdk import MappedPDK +from glayout.pdk.sky130_mapped import sky130_mapped_pdk from gdsfactory.cell import cell from gdsfactory.component import Component from gdsfactory.component_reference import ComponentReference from gdsfactory import Component -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.L_route import L_route -from glayout.flow.primitives.guardring import tapring -from glayout.flow.pdk.util.port_utils import add_ports_perimeter, rename_ports_by_list -from glayout.flow.spice.netlist import Netlist -from glayout.flow.primitives.via_gen import via_stack +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center +from glayout.util.snap_to_grid import component_snap_to_grid +from glayout.util.port_utils import rename_ports_by_orientation +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route +from glayout.primitives.guardring import tapring +from glayout.util.port_utils import add_ports_perimeter, rename_ports_by_list +from glayout.spice.netlist import Netlist +from glayout.primitives.via_gen import via_stack from gdsfactory.components import text_freetype, rectangle -from glayout.flow.placement.four_transistor_interdigitized import generic_4T_interdigitzed +from glayout.placement.four_transistor_interdigitized import generic_4T_interdigitzed def p_block_netlist(pdk: MappedPDK, pblock: tuple[float, float, int]) -> Netlist: return Netlist( diff --git a/src/glayout/blocks/composite/fvf_based_ota/sky130_ota_tapeout.py b/src/glayout/blocks/composite/fvf_based_ota/sky130_ota_tapeout.py index c5eb5230..fb86231c 100644 --- a/src/glayout/blocks/composite/fvf_based_ota/sky130_ota_tapeout.py +++ b/src/glayout/blocks/composite/fvf_based_ota/sky130_ota_tapeout.py @@ -6,20 +6,20 @@ from gdsfactory.read.import_gds import import_gds from gdsfactory.components import text_freetype, rectangle -from glayout.flow.pdk.util.comp_utils import prec_array, movey, align_comp_to_port, prec_ref_center -from glayout.flow.pdk.util.port_utils import add_ports_perimeter, print_ports +from glayout.util.comp_utils import prec_array, movey, align_comp_to_port, prec_ref_center +from glayout.util.port_utils import add_ports_perimeter, print_ports from gdsfactory.component import Component -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.blocks.composite.fvf_based_ota.ota import super_class_AB_OTA -from glayout.flow.routing.L_route import L_route -from glayout.flow.routing.c_route import c_route -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_ref_center, prec_center, align_comp_to_port -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.pdk.mappedpdk import MappedPDK +from glayout.blocks.composite.fvf_based_ota.ota import super_class_AB_OTA +from glayout.routing.L_route import L_route +from glayout.routing.c_route import c_route +from glayout.routing.straight_route import straight_route +from glayout.util.comp_utils import evaluate_bbox, prec_ref_center, prec_center, align_comp_to_port +from glayout.util.port_utils import rename_ports_by_orientation +from glayout.util.snap_to_grid import component_snap_to_grid from gdsfactory.components import text_freetype, rectangle -from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.primitives.via_gen import via_array, via_stack +from glayout.pdk.mappedpdk import MappedPDK +from glayout.primitives.via_gen import via_array, via_stack from gdsfactory.cell import cell, clear_cache import numpy as np from subprocess import Popen @@ -38,9 +38,9 @@ from sklearn.cluster import KMeans, AgglomerativeClustering from sklearn.metrics import silhouette_score import argparse -from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as pdk +from glayout.pdk.sky130_mapped import sky130_mapped_pdk as pdk from itertools import count, repeat -from glayout.flow.pdk.util.component_array_create import write_component_matrix +from glayout.util.component_array_create import write_component_matrix import re import pickle import tempfile @@ -611,7 +611,7 @@ def single_build_and_simulation(parameters: np.array, temp: int=25, output_dir: saves ota gds in current directory with name 12345678987654321.gds returns -987.654321 for all values IF phase margin < 60 """ - from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk + from glayout.pdk.sky130_mapped import sky130_mapped_pdk # process temperature info temperature_info = [temp, None] if temperature_info[0] > -20: @@ -810,7 +810,7 @@ def get_parameter_value(param_name: str, *args, **kwargs): elif args.mode == "gen_otas": global usepdk if args.pdk[0].lower()=="g": - from glayout.flow.pdk.gf180_mapped import gf180_mapped_pdk + from glayout.pdk.gf180_mapped import gf180_mapped_pdk usepdk = gf180_mapped_pdk else: usepdk = pdk diff --git a/src/glayout/blocks/composite/low_voltage_cmirror/__init__.py b/src/glayout/blocks/composite/low_voltage_cmirror/__init__.py index 7fa87ec0..c2f48562 100644 --- a/src/glayout/blocks/composite/low_voltage_cmirror/__init__.py +++ b/src/glayout/blocks/composite/low_voltage_cmirror/__init__.py @@ -1 +1 @@ -from glayout.blocks.elementary.low_voltage_cmirror.low_voltage_cmirror import low_voltage_cmirror, low_voltage_cmirr_netlist \ No newline at end of file +from glayout.blocks.composite.low_voltage_cmirror.low_voltage_cmirror import low_voltage_cmirror, low_voltage_cmirr_netlist \ No newline at end of file diff --git a/src/glayout/blocks/composite/low_voltage_cmirror/low_voltage_cmirror.py b/src/glayout/blocks/composite/low_voltage_cmirror/low_voltage_cmirror.py index b88bd077..40973eca 100644 --- a/src/glayout/blocks/composite/low_voltage_cmirror/low_voltage_cmirror.py +++ b/src/glayout/blocks/composite/low_voltage_cmirror/low_voltage_cmirror.py @@ -1,27 +1,36 @@ -from glayout import MappedPDK, sky130,gf180 -from glayout import nmos, pmos, tapring,via_stack -from glayout.spice.netlist import Netlist -from glayout.routing import c_route,L_route,straight_route +from glayout.pdk.mappedpdk import MappedPDK +from glayout.pdk.sky130_mapped import sky130_mapped_pdk from gdsfactory.component import Component from gdsfactory.component_reference import ComponentReference from gdsfactory.cell import cell from gdsfactory import Component from gdsfactory.components import text_freetype, rectangle +from glayout.primitives.fet import nmos, pmos, multiplier from glayout.util.comp_utils import evaluate_bbox, prec_center, align_comp_to_port, prec_ref_center from glayout.util.snap_to_grid import component_snap_to_grid from glayout.util.port_utils import rename_ports_by_orientation +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route +from glayout.primitives.guardring import tapring from glayout.util.port_utils import add_ports_perimeter -from glayout.blocks.elementary.FVF import fvf_netlist, flipped_voltage_follower - +from glayout.spice.netlist import Netlist +from glayout.blocks.elementary.FVF.fvf import fvf_netlist, flipped_voltage_follower # Import from local ATLAS fvf.py from glayout.primitives.via_gen import via_stack from typing import Optional -import time +from glayout.blocks.evaluator_box.evaluator_wrapper import run_evaluation + def add_lvcm_labels(lvcm_in: Component, pdk: MappedPDK ) -> Component: lvcm_in.unlock() + + met2_pin = (68,16) + met2_label = (68,5) + met3_pin = (69,16) + met3_label = (69,5) # list that will contain all port/comp info move_info = list() # create labels and append to info list @@ -58,8 +67,9 @@ def add_lvcm_labels(lvcm_in: Component, def low_voltage_cmirr_netlist(bias_fvf: Component, cascode_fvf: Component, fet_1_ref: ComponentReference, fet_2_ref: ComponentReference, fet_3_ref: ComponentReference, fet_4_ref: ComponentReference) -> Netlist: netlist = Netlist(circuit_name='Low_voltage_current_mirror', nodes=['IBIAS1', 'IBIAS2', 'GND', 'IOUT1', 'IOUT2']) - netlist.connect_netlist(bias_fvf.info['netlist'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib','IBIAS1'),('VOUT','local_net_1')]) - netlist.connect_netlist(cascode_fvf.info['netlist'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib', 'IBIAS2'),('VOUT','local_net_2')]) + # Use netlist_obj for hierarchical netlist building + netlist.connect_netlist(bias_fvf.info['netlist_obj'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib','IBIAS1'),('VOUT','local_net_1')]) + netlist.connect_netlist(cascode_fvf.info['netlist_obj'], [('VIN','IBIAS1'),('VBULK','GND'),('Ib', 'IBIAS2'),('VOUT','local_net_2')]) fet_1A_ref=netlist.connect_netlist(fet_2_ref.info['netlist'], [('D', 'IOUT1'),('G','IBIAS1'),('B','GND')]) fet_2A_ref=netlist.connect_netlist(fet_4_ref.info['netlist'], [('D', 'IOUT2'),('G','IBIAS1'),('B','GND')]) fet_1B_ref=netlist.connect_netlist(fet_1_ref.info['netlist'], [('G','IBIAS2'),('S', 'GND'),('B','GND')]) @@ -175,25 +185,17 @@ def low_voltage_cmirror( top_level.add_ports(fet_4_ref.get_ports_list(), prefix="M_4_A_") component = component_snap_to_grid(rename_ports_by_orientation(top_level)) - component.info['netlist'] = low_voltage_cmirr_netlist(bias_fvf, cascode_fvf, fet_1_ref, fet_2_ref, fet_3_ref, fet_4_ref) + netlist_obj = low_voltage_cmirr_netlist(bias_fvf, cascode_fvf, fet_1_ref, fet_2_ref, fet_3_ref, fet_4_ref) + component.info['netlist'] = netlist_obj.generate_netlist() return component -if __name__ == "__main__": - comp =low_voltage_cmirror(sky130) - # comp.pprint_ports() - comp =add_lvcm_labels(comp,sky130) - comp.name = "LVCM" - comp.show() - #print(comp.info['netlist'].generate_netlist()) - print("...Running DRC...") - drc_result = sky130.drc_magic(comp, "LVCM") - ## Klayout DRC - #drc_result = sky130.drc(comp) - - time.sleep(5) - - print("...Running LVS...") - lvs_res=sky130.lvs_netgen(comp, "LVCM") - #print("...Saving GDS...") - #comp.write_gds('out_LVCM.gds') +if __name__=="__main__": + #low_voltage_current_mirror = low_voltage_current_mirror(sky130_mapped_pdk) + low_voltage_current_mirror = add_lvcm_labels(low_voltage_cmirror(sky130_mapped_pdk),sky130_mapped_pdk) + low_voltage_current_mirror.show() + low_voltage_current_mirror.name = "Low_voltage_current_mirror" + #magic_drc_result = sky130_mapped_pdk.drc_magic(low_voltage_current_mirror, low_voltage_current_mirror.name) + #netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(low_voltage_current_mirror, low_voltage_current_mirror.name) + low_voltage_current_mirror_gds = low_voltage_current_mirror.write_gds("low_voltage_current_mirror.gds") + res = run_evaluation("low_voltage_current_mirror.gds", low_voltage_current_mirror.name, low_voltage_current_mirror) \ No newline at end of file diff --git a/src/glayout/blocks/composite/opamp/__init__.py b/src/glayout/blocks/composite/opamp/__init__.py index 48801bcb..ee1d6902 100644 --- a/src/glayout/blocks/composite/opamp/__init__.py +++ b/src/glayout/blocks/composite/opamp/__init__.py @@ -1,2 +1,2 @@ -from glayout.flow.blocks.composite.opamp.opamp import opamp, opamp_netlist -from glayout.flow.blocks.composite.opamp.diff_pair_stackedcmirror import diff_pair_stackedcmirror \ No newline at end of file +from glayout.blocks.composite.opamp.opamp import opamp, opamp_netlist +from glayout.blocks.composite.opamp.diff_pair_stackedcmirror import diff_pair_stackedcmirror \ No newline at end of file diff --git a/src/glayout/blocks/composite/opamp/diff_pair_stackedcmirror.py b/src/glayout/blocks/composite/opamp/diff_pair_stackedcmirror.py index e366146a..6b42b27c 100644 --- a/src/glayout/blocks/composite/opamp/diff_pair_stackedcmirror.py +++ b/src/glayout/blocks/composite/opamp/diff_pair_stackedcmirror.py @@ -2,27 +2,27 @@ from gdsfactory.component import Component, copy from gdsfactory.component_reference import ComponentReference from gdsfactory.components.rectangle import rectangle -from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.flow.blocks.elementary.diff_pair import diff_pair -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.primitives.guardring import tapring -from glayout.flow.primitives.mimcap import mimcap_array, mimcap -from glayout.flow.primitives.via_gen import via_stack, via_array -from glayout.flow.routing.L_route import L_route -from glayout.flow.routing.c_route_old import c_route +from glayout.blocks.elementary.diff_pair import diff_pair +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.primitives.guardring import tapring +from glayout.primitives.mimcap import mimcap_array, mimcap +from glayout.primitives.via_gen import via_stack, via_array +from glayout.routing.L_route import L_route +from glayout.routing.c_route import c_route from gdsfactory.routing.route_quad import route_quad -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc +from glayout.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports +from glayout.routing.straight_route import straight_route +from glayout.util.snap_to_grid import component_snap_to_grid from pydantic import validate_arguments -from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.flow.blocks.composite.diffpair_cmirror_bias import diff_pair_ibias -from glayout.flow.blocks.composite.stacked_current_mirror import stacked_nfet_current_mirror -from glayout.flow.blocks.composite.differential_to_single_ended_converter import differential_to_single_ended_converter -from glayout.flow.blocks.composite.opamp.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter +from glayout.blocks.composite.diffpair_cmirror_bias import diff_pair_ibias +from glayout.blocks.composite.stacked_current_mirror import stacked_nfet_current_mirror +from glayout.blocks.composite.differential_to_single_ended_converter import differential_to_single_ended_converter +from glayout.blocks.composite.opamp.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter @validate_arguments diff --git a/src/glayout/blocks/composite/opamp/opamp.py b/src/glayout/blocks/composite/opamp/opamp.py index 8fdf6208..03adf612 100644 --- a/src/glayout/blocks/composite/opamp/opamp.py +++ b/src/glayout/blocks/composite/opamp/opamp.py @@ -2,26 +2,26 @@ from gdsfactory.component import Component, copy from gdsfactory.component_reference import ComponentReference from gdsfactory.components.rectangle import rectangle -from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.blocks.elementary.diff_pair import diff_pair -from glayout.flow.primitives.guardring import tapring -from glayout.flow.primitives.mimcap import mimcap_array, mimcap -from glayout.flow.routing.L_route import L_route -from glayout.flow.routing.c_route_old import c_route -from glayout.flow.primitives.via_gen import via_stack, via_array +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.blocks.elementary.diff_pair import diff_pair +from glayout.primitives.guardring import tapring +from glayout.primitives.mimcap import mimcap_array, mimcap +from glayout.routing.L_route import L_route +from glayout.routing.c_route import c_route +from glayout.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc +from glayout.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports +from glayout.routing.straight_route import straight_route +from glayout.util.snap_to_grid import component_snap_to_grid from pydantic import validate_arguments -from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.flow.spice import Netlist +from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.spice import Netlist -from glayout.flow.blocks.composite.opamp.opamp_twostage import opamp_twostage -from glayout.flow.blocks.elementary.current_mirror import current_mirror_netlist +from glayout.blocks.composite.opamp.opamp_twostage import opamp_twostage +from glayout.blocks.elementary.current_mirror import current_mirror_netlist def opamp_output_stage_netlist(pdk: MappedPDK, output_amp_fet_ref: ComponentReference, biasParams: list) -> Netlist: bias_netlist = current_mirror_netlist(pdk, biasParams[0], biasParams[1], biasParams[2]) diff --git a/src/glayout/blocks/composite/opamp/opamp_twostage.py b/src/glayout/blocks/composite/opamp/opamp_twostage.py index 5dcbcc4b..efaee3a5 100644 --- a/src/glayout/blocks/composite/opamp/opamp_twostage.py +++ b/src/glayout/blocks/composite/opamp/opamp_twostage.py @@ -2,30 +2,30 @@ from gdsfactory.component import Component, copy from gdsfactory.component_reference import ComponentReference from gdsfactory.components.rectangle import rectangle -from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.blocks.elementary.diff_pair import diff_pair -from glayout.flow.primitives.guardring import tapring -from glayout.flow.primitives.mimcap import mimcap_array, mimcap -from glayout.flow.routing.L_route import L_route -from glayout.flow.routing.c_route_old import c_route -from glayout.flow.primitives.via_gen import via_stack, via_array +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.blocks.elementary.diff_pair import diff_pair +from glayout.primitives.guardring import tapring +from glayout.primitives.mimcap import mimcap_array, mimcap +from glayout.routing.L_route import L_route +from glayout.routing.c_route import c_route +from glayout.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc +from glayout.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports +from glayout.routing.straight_route import straight_route +from glayout.util.snap_to_grid import component_snap_to_grid from pydantic import validate_arguments -from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized - -from glayout.flow.blocks.composite.diffpair_cmirror_bias import diff_pair_ibias -from glayout.flow.blocks.composite.stacked_current_mirror import stacked_nfet_current_mirror -from glayout.flow.blocks.composite.differential_to_single_ended_converter import differential_to_single_ended_converter -from glayout.flow.blocks.composite.opamp.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter -from glayout.flow.blocks.composite.opamp.diff_pair_stackedcmirror import diff_pair_stackedcmirror -from glayout.flow.spice import Netlist -from glayout.flow.blocks.elementary.current_mirror import current_mirror_netlist +from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized + +from glayout.blocks.composite.diffpair_cmirror_bias import diff_pair_ibias +from glayout.blocks.composite.stacked_current_mirror import stacked_nfet_current_mirror +from glayout.blocks.composite.differential_to_single_ended_converter import differential_to_single_ended_converter +from glayout.blocks.composite.opamp.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter +from glayout.blocks.composite.opamp.diff_pair_stackedcmirror import diff_pair_stackedcmirror +from glayout.spice import Netlist +from glayout.blocks.elementary.current_mirror import current_mirror_netlist @validate_arguments def __create_and_route_pins( diff --git a/src/glayout/blocks/composite/opamp/row_csamplifier_diff_to_single_ended_converter.py b/src/glayout/blocks/composite/opamp/row_csamplifier_diff_to_single_ended_converter.py index 26a4e5b0..62768c57 100644 --- a/src/glayout/blocks/composite/opamp/row_csamplifier_diff_to_single_ended_converter.py +++ b/src/glayout/blocks/composite/opamp/row_csamplifier_diff_to_single_ended_converter.py @@ -2,23 +2,23 @@ from gdsfactory.component import Component, copy from gdsfactory.component_reference import ComponentReference from gdsfactory.components.rectangle import rectangle -from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.flow.primitives.fet import nmos, pmos, multiplier -from glayout.flow.blocks.elementary.diff_pair import diff_pair -from glayout.flow.primitives.guardring import tapring -from glayout.flow.primitives.mimcap import mimcap_array, mimcap -from glayout.flow.routing.L_route import L_route -from glayout.flow.routing.c_route_old import c_route -from glayout.flow.primitives.via_gen import via_stack, via_array +from glayout.primitives.fet import nmos, pmos, multiplier +from glayout.blocks.elementary.diff_pair import diff_pair +from glayout.primitives.guardring import tapring +from glayout.primitives.mimcap import mimcap_array, mimcap +from glayout.routing.L_route import L_route +from glayout.routing.c_route import c_route +from glayout.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc -from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports -from glayout.flow.routing.straight_route import straight_route -from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.util.comp_utils import evaluate_bbox, prec_ref_center, movex, movey, to_decimal, to_float, move, align_comp_to_port, get_padding_points_cc +from glayout.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, set_port_orientation, rename_component_ports +from glayout.routing.straight_route import straight_route +from glayout.util.snap_to_grid import component_snap_to_grid from pydantic import validate_arguments -from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.flow.spice import Netlist +from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.spice import Netlist def row_csamplifier_diff_to_single_ended_converter_netlist(diff_to_single: Component) -> Netlist: overall_netlist = Netlist( diff --git a/src/glayout/blocks/elementary/FVF/README.md b/src/glayout/blocks/elementary/FVF/README.md deleted file mode 100644 index 5e375a45..00000000 --- a/src/glayout/blocks/elementary/FVF/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# FLIPPED VOLTAGE FOLLOWER CELL - -AL: Sep 29 2025 - -Migrated from Arnav's fork of OpenFASOC -- Updated code to make it compatible with latest new gLayout repo -- The evaluator_wrapper is broken, need to be fixed - -## Schematic -![FVF schematic](https://github.com/user-attachments/assets/369d8073-e5bb-4e17-96f4-22f496d5663a) - -A flipped voltage follower contains two fets connected as seen in this schematic. One is the input fet, the other one is the feedback fet. -## Parametrizing the FVF block -``` -def flipped_voltage_follower( - pdk: MappedPDK, - device_type: str = "nmos", - placement: str = "horizontal", - width: tuple[float,float] = (3,3), - length: tuple[float,float] = (None,None), - fingers: tuple[int,int] = (1,1), - multipliers: tuple[int,int] = (1,1), - dummy_1: tuple[bool,bool] = (True,True), - dummy_2: tuple[bool,bool] = (True,True), - tie_layers1: tuple[str,str] = ("met2","met1"), - tie_layers2: tuple[str,str] = ("met2","met1"), - sd_rmult: int=1, - **kwargs - ) -> Component: - """ - creates a Flipped Voltage Follower - pdk: pdk to use - device_type: either "nmos" or "pmos" - placement: either "horizontal" or "vertical" - width: (input fet, feedback fet) - length: (input fet, feedback fet) - fingers: (input fet, feedback fet) - multipliers: (input fet, feedback fet) - dummy_1: dummy for input fet - dummy_2: dummy for feedback fet - tie_layers1: tie layers for input fet - tie_layers2: tie layers for feedback fet - sd_rmult: sd_rmult for both fets - **kwargs: any kwarg that is supported by nmos and pmos -``` -### GDS generated -![gds generated](./fvfgds.png) -### DRC Report -``` -using default pdk_root: /usr/bin/miniconda3/share/pdk/ -Defaulting to stale magic_commands.tcl - -Magic 8.3 revision 464 - Compiled on Sat Mar 9 23:18:29 UTC 2024. -Starting magic under Tcl interpreter -Using the terminal as the console. -Using NULL graphics device. -Processing system .magicrc file -Sourcing design .magicrc for technology sky130A ... -2 Magic internal units = 1 Lambda -Input style sky130(): scaleFactor=2, multiplier=2 -The following types are not handled by extraction and will be treated as non-electrical types: - ubm -Scaled tech values by 2 / 1 to match internal grid scaling -Loading sky130A Device Generator Menu ... -Loading "/tmp/tmp0t0g30yo/magic_commands.tcl" from command line. -Warning: Calma reading is not undoable! I hope that's OK. -Library written using GDS-II Release 6.0 -Library name: library -Reading "fvf". -[INFO]: Loading fvf - -Loading DRC CIF style. -No errors found. -[INFO]: DONE with /tmp/tmp0t0g30yo/fvf.rpt - -Using technology "sky130A", version 1.0.471-0-g97d0844 - -Soft errors: -Error while reading cell "fvf" (byte position 118): Unknown layer/datatype in boundary, layer=64 type=44 -``` -### LVS Report -``` -Circuit 1 cell sky130_fd_pr__nfet_01v8 and Circuit 2 cell sky130_fd_pr__nfet_01v8 are black boxes. -Warning: Equate pins: cell sky130_fd_pr__nfet_01v8 is a placeholder, treated as a black box. -Warning: Equate pins: cell sky130_fd_pr__nfet_01v8 is a placeholder, treated as a black box. - -Subcircuit pins: -Circuit 1: sky130_fd_pr__nfet_01v8 |Circuit 2: sky130_fd_pr__nfet_01v8 --------------------------------------------|------------------------------------------- -1 |1 -2 |2 -3 |3 -4 |4 ---------------------------------------------------------------------------------------- -Cell pin lists are equivalent. -Device classes sky130_fd_pr__nfet_01v8 and sky130_fd_pr__nfet_01v8 are equivalent. -Flattening unmatched subcell NMOS in circuit fvf (1)(2 instances) - -Class fvf (0): Merged 3 parallel devices. -Class fvf (1): Merged 3 parallel devices. -Subcircuit summary: -Circuit 1: fvf |Circuit 2: fvf --------------------------------------------|------------------------------------------- -sky130_fd_pr__nfet_01v8 (6->3) |sky130_fd_pr__nfet_01v8 (6->3) -Number of devices: 3 |Number of devices: 3 -Number of nets: 4 |Number of nets: 4 ---------------------------------------------------------------------------------------- -Netlists match uniquely. - -Subcircuit pins: -Circuit 1: fvf |Circuit 2: fvf --------------------------------------------|------------------------------------------- -VIN |VIN -Ib |Ib -VOUT |VOUT -VBULK |VBULK ---------------------------------------------------------------------------------------- -Cell pin lists are equivalent. -Device classes fvf and fvf are equivalent. - -Final result: Circuits match uniquely. -``` diff --git a/src/glayout/blocks/elementary/FVF/__init__.py b/src/glayout/blocks/elementary/FVF/__init__.py index fd3e01c7..2d88f2fd 100644 --- a/src/glayout/blocks/elementary/FVF/__init__.py +++ b/src/glayout/blocks/elementary/FVF/__init__.py @@ -1 +1 @@ -from glayout.blocks.elementary.FVF.fvf import flipped_voltage_follower, fvf_netlist, add_fvf_labels \ No newline at end of file +from glayout.blocks.elementary.FVF.fvf import flipped_voltage_follower, fvf_netlist, sky130_add_fvf_labels \ No newline at end of file diff --git a/src/glayout/blocks/elementary/FVF/evaluator_wrapper.py b/src/glayout/blocks/elementary/FVF/evaluator_wrapper.py deleted file mode 100644 index 893a6bfc..00000000 --- a/src/glayout/blocks/elementary/FVF/evaluator_wrapper.py +++ /dev/null @@ -1,76 +0,0 @@ -# comprehensive evaluator -import os -import json -import logging -from datetime import datetime -from pathlib import Path -from gdsfactory.typings import Component - -from glayout.blocks.evaluator_box.verification import run_verification -from glayout.blocks.evaluator_box.physical_features import run_physical_feature_extraction - -def get_next_filename(base_name="evaluation", extension=".json"): - """ - Generates the next available filename with a numerical suffix, starting from 1. - e.g., base_name_1.json, base_name_2.json, etc. - """ - i = 1 - while True: - filename = f"{base_name}_{i}{extension}" - if not os.path.exists(filename): - return filename - i += 1 - -def run_evaluation(layout_path: str, component_name: str, top_level: Component) -> dict: - """ - The main evaluation wrapper. Runs all evaluation modules and combines results. - """ - print(f"--- Starting Comprehensive Evaluation for {component_name} ---") - - # Deletes known intermediate and report files for a given component to ensure a clean run. - print(f"Cleaning up intermediate files for component '{component_name}'...") - - files_to_delete = [ - f"{component_name}.res.ext", - f"{component_name}.lvs.rpt", - f"{component_name}_lvs.rpt", - f"{component_name}.nodes", - f"{component_name}.sim", - f"{component_name}.pex.spice", - f"{component_name}_pex.spice" - ] - - for f_path in files_to_delete: - try: - if os.path.exists(f_path): - os.remove(f_path) - print(f" - Deleted: {f_path}") - except OSError as e: - print(f" - Warning: Could not delete {f_path}. Error: {e}") - - # Run verification module - print("Running verification checks (DRC, LVS)...") - verification_results = run_verification(layout_path, component_name, top_level) - - # Run physical features module - print("Running physical feature extraction (PEX, Area, Symmetry)...") - physical_results = run_physical_feature_extraction(layout_path, component_name, top_level) - - # Combine results into a single dictionary - final_results = { - "component_name": component_name, - "timestamp": datetime.now().isoformat(), - "drc_lvs_fail": not (verification_results["drc"]["is_pass"] and verification_results["lvs"]["is_pass"]), - **verification_results, - **physical_results - } - - # Generate the output JSON filename - output_filename = get_next_filename(base_name=component_name, extension=".json") - - # Write the results dictionary to a JSON file - with open(output_filename, 'w') as json_file: - json.dump(final_results, json_file, indent=4) - print(f"--- Evaluation complete. Results saved to {output_filename} ---") - - return final_results diff --git a/src/glayout/blocks/elementary/FVF/fvf.cdl b/src/glayout/blocks/elementary/FVF/fvf.cdl deleted file mode 100644 index a60e694b..00000000 --- a/src/glayout/blocks/elementary/FVF/fvf.cdl +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/glayout/blocks/elementary/FVF/fvf.py b/src/glayout/blocks/elementary/FVF/fvf.py index 9afff8b7..b244cb04 100644 --- a/src/glayout/blocks/elementary/FVF/fvf.py +++ b/src/glayout/blocks/elementary/FVF/fvf.py @@ -1,6 +1,5 @@ -from glayout import MappedPDK, sky130,gf180 -from glayout.spice.netlist import Netlist -from glayout.routing import c_route,L_route,straight_route +from glayout.pdk.mappedpdk import MappedPDK +from glayout.pdk.sky130_mapped import sky130_mapped_pdk from gdsfactory.cell import cell from gdsfactory.component import Component from gdsfactory import Component @@ -8,18 +7,50 @@ from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port from glayout.util.snap_to_grid import component_snap_to_grid from glayout.util.port_utils import rename_ports_by_orientation +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route from glayout.primitives.guardring import tapring from glayout.util.port_utils import add_ports_perimeter +from glayout.spice.netlist import Netlist from glayout.primitives.via_gen import via_stack from gdsfactory.components import text_freetype, rectangle -from glayout.blocks.elementary.FVF.evaluator_wrapper import run_evaluation # This is broken, need to be fixed +try: + from glayout.blocks.evaluator_box.evaluator_wrapper import run_evaluation +except ImportError: + print("Warning: evaluator_wrapper not found. Evaluation will be skipped.") + run_evaluation = None + +def get_component_netlist(component): + """Helper function to get netlist object from component info, compatible with all gdsfactory versions""" + from glayout.spice.netlist import Netlist + + # Try to get stored object first (for older gdsfactory versions) + if 'netlist_obj' in component.info: + return component.info['netlist_obj'] + + # Try to reconstruct from netlist_data (for newer gdsfactory versions) + if 'netlist_data' in component.info: + data = component.info['netlist_data'] + netlist = Netlist( + circuit_name=data['circuit_name'], + nodes=data['nodes'] + ) + netlist.source_netlist = data['source_netlist'] + return netlist + + # Fallback: return the string representation (should not happen in normal operation) + return component.info.get('netlist', '') def fvf_netlist(fet_1: Component, fet_2: Component) -> Netlist: netlist = Netlist(circuit_name='FLIPPED_VOLTAGE_FOLLOWER', nodes=['VIN', 'VBULK', 'VOUT', 'Ib']) - netlist.connect_netlist(fet_1.info['netlist'], [('D', 'Ib'), ('G', 'VIN'), ('S', 'VOUT'), ('B', 'VBULK')]) - netlist.connect_netlist(fet_2.info['netlist'], [('D', 'VOUT'), ('G', 'Ib'), ('S', 'VBULK'), ('B', 'VBULK')]) + # Use helper function to get netlist objects regardless of gdsfactory version + fet_1_netlist = get_component_netlist(fet_1) + fet_2_netlist = get_component_netlist(fet_2) + netlist.connect_netlist(fet_1_netlist, [('D', 'Ib'), ('G', 'VIN'), ('S', 'VOUT'), ('B', 'VBULK')]) + netlist.connect_netlist(fet_2_netlist, [('D', 'VOUT'), ('G', 'Ib'), ('S', 'VBULK'), ('B', 'VBULK')]) return netlist @@ -63,13 +94,13 @@ def sky130_add_fvf_labels(fvf_in: Component) -> Component: @cell def flipped_voltage_follower( - pdk: MappedPDK, - device_type: str = "nmos", - placement: str = "horizontal", - width: tuple[float,float] = (3,3), - length: tuple[float,float] = (None,None), - fingers: tuple[int,int] = (1,1), - multipliers: tuple[int,int] = (1,1), + pdk: MappedPDK, + device_type: str = "nmos", + placement: str = "horizontal", + width: tuple[float,float] = (6.605703928526579, 3.713220935212418), + length: tuple[float,float] = (2.3659471990041707, 1.9639325665440608), + fingers: tuple[int,int] = (1, 1), + multipliers: tuple[int,int] = (2, 2), dummy_1: tuple[bool,bool] = (True,True), dummy_2: tuple[bool,bool] = (True,True), tie_layers1: tuple[str,str] = ("met2","met1"), @@ -111,7 +142,6 @@ def flipped_voltage_follower( fet_1 = device(pdk, width=width[0], fingers=fingers[0], multipliers=multipliers[0], with_dummy=dummy_1, with_substrate_tap=False, length=length[0], tie_layers=tie_layers1, sd_rmult=sd_rmult, **kwargs) fet_2 = device(pdk, width=width[1], fingers=fingers[1], multipliers=multipliers[1], with_dummy=dummy_2, with_substrate_tap=False, length=length[1], tie_layers=tie_layers2, sd_rmult=sd_rmult, **kwargs) well = "pwell" if device == nmos else "nwell" - fet_1_ref = top_level << fet_1 fet_2_ref = top_level << fet_2 @@ -157,66 +187,25 @@ def flipped_voltage_follower( component = component_snap_to_grid(rename_ports_by_orientation(top_level)) #component = rename_ports_by_orientation(top_level) - component.info['netlist'] = fvf_netlist(fet_1, fet_2) + # Store netlist as string for LVS (avoids gymnasium info dict type restrictions) + # Compatible with both gdsfactory 7.7.0 and 7.16.0+ strict Pydantic validation + netlist_obj = fvf_netlist(fet_1, fet_2) + component.info['netlist'] = netlist_obj.generate_netlist() + # Store the Netlist object for hierarchical netlist building (used by lvcm.py etc.) + component.info['netlist_obj'] = netlist_obj + # Store serialized netlist data for reconstruction if needed + component.info['netlist_data'] = { + 'circuit_name': netlist_obj.circuit_name, + 'nodes': netlist_obj.nodes, + 'source_netlist': netlist_obj.source_netlist + } return component -if __name__ == "__main__": - # OLD EVAL CODE - # comp = flipped_voltage_follower(sky130) - # # comp.pprint_ports() - # comp = add_fvf_labels(comp,sky130) - # comp.name = "FVF" - # comp.show() - # #print(comp.info['netlist'].generate_netlist()) - # print("...Running DRC...") - # drc_result = sky130.drc_magic(comp, "FVF") - # ## Klayout DRC - # #drc_result = sky130.drc(comp)\n - - # time.sleep(5) - - # print("...Running LVS...") - # lvs_res=sky130.lvs_netgen(comp, "FVF") - # #print("...Saving GDS...") - # #comp.write_gds('out_FVF.gds') - - # NEW EVAL CODE - fvf = sky130_add_fvf_labels(flipped_voltage_follower(sky130, width=(2,1), sd_rmult=3)) +if __name__=="__main__": + fvf = sky130_add_fvf_labels(flipped_voltage_follower(sky130_mapped_pdk, width=(2,1), sd_rmult=3)) fvf.show() fvf.name = "fvf" fvf_gds = fvf.write_gds("fvf.gds") - - # Debug: Check if netlist exists and print it - if 'netlist' in fvf.info: - print("Netlist found in component info:") - - # Try to get stored object first (for older gdsfactory versions) - if 'netlist_obj' in fvf.info: - netlist_obj = fvf.info['netlist_obj'] - netlist_content = netlist_obj.generate_netlist() - # Try to reconstruct from netlist_data (for newer gdsfactory versions) - elif 'netlist_data' in fvf.info: - data = fvf.info['netlist_data'] - netlist_obj = Netlist( - circuit_name=data['circuit_name'], - nodes=data['nodes'] - ) - netlist_obj.source_netlist = data['source_netlist'] - netlist_content = netlist_obj.generate_netlist() - else: - # Fallback: if it's already a string, use it directly - netlist_content = str(fvf.info['netlist']) - - print(netlist_content) - print("Writing netlist to fvf.cdl for inspection...") - with open("fvf.cdl", "w") as f: - f.write(netlist_content) - else: - print("No netlist found in component info!") - result = run_evaluation("fvf.gds",fvf.name,fvf) - print(result) - - - + print(result) \ No newline at end of file diff --git a/src/glayout/blocks/elementary/FVF/physical_features.py b/src/glayout/blocks/elementary/FVF/physical_features.py deleted file mode 100644 index 2c32978d..00000000 --- a/src/glayout/blocks/elementary/FVF/physical_features.py +++ /dev/null @@ -1,111 +0,0 @@ -# physical_features.py -import os -import re -import subprocess -import shutil -from pathlib import Path -from gdsfactory.typings import Component -from gdsfactory.geometry.boolean import boolean - -def calculate_area(component: Component) -> float: - """Calculates the area of a gdsfactory Component.""" - return float(component.area()) - -def _mirror_and_xor(component: Component, axis: str) -> float: - """Helper to perform mirroring and XOR for symmetry calculation.""" - # --- Operate on a copy to prevent modifying the original --- - comp_copy = component.copy() - comp_copy.unlock() - - mirrored_ref = comp_copy.copy() - if axis == 'vertical': - mirrored_ref = mirrored_ref.mirror((0, -100), (0, 100)) - elif axis == 'horizontal': - mirrored_ref = mirrored_ref.mirror((-100, 0), (100, 0)) - else: - return 0.0 - - # Pass the copies to the boolean operation - asymmetry_layout = boolean(A=comp_copy, B=mirrored_ref, operation="xor") - return float(asymmetry_layout.area()) - -def calculate_symmetry_scores(component: Component) -> tuple[float, float]: - """Calculates horizontal and vertical symmetry scores (1.0 = perfect symmetry).""" - original_area = calculate_area(component) - if original_area == 0: - return (1.0, 1.0) - - asymmetry_y_area = _mirror_and_xor(component, 'horizontal') - asymmetry_x_area = _mirror_and_xor(component, 'vertical') - - symmetry_score_horizontal = 1.0 - (asymmetry_x_area / original_area) - symmetry_score_vertical = 1.0 - (asymmetry_y_area / original_area) - return symmetry_score_horizontal, symmetry_score_vertical - -def _parse_simple_parasitics(component_name: str) -> tuple[float, float]: - """Parses total parasitic R and C from a SPICE file by simple summation.""" - total_resistance = 0.0 - total_capacitance = 0.0 - spice_file_path = f"{component_name}_pex.spice" - if not os.path.exists(spice_file_path): - return 0.0, 0.0 - with open(spice_file_path, 'r') as f: - for line in f: - line = line.strip().upper() - parts = line.split() - if not parts: continue - - name = parts[0] - if name.startswith('R') and len(parts) >= 4: - try: total_resistance += float(parts[3]) - except (ValueError): continue - elif name.startswith('C') and len(parts) >= 4: - try: - cap_str = parts[3] - unit = cap_str[-1] - val_str = cap_str[:-1] - if unit == 'F': cap_value = float(val_str) * 1e-15 - elif unit == 'P': cap_value = float(val_str) * 1e-12 - elif unit == 'N': cap_value = float(val_str) * 1e-9 - elif unit == 'U': cap_value = float(val_str) * 1e-6 - else: cap_value = float(cap_str) - total_capacitance += cap_value - except (ValueError): continue - return total_resistance, total_capacitance - -def run_physical_feature_extraction(layout_path: str, component_name: str, top_level: Component) -> dict: - """ - Runs PEX and calculates geometric features, returning a structured result. - """ - physical_results = { - "pex": {"status": "not run", "total_resistance_ohms": 0.0, "total_capacitance_farads": 0.0}, - "geometric": {"raw_area_um2": 0.0, "symmetry_score_horizontal": 0.0, "symmetry_score_vertical": 0.0} - } - - # PEX and Parasitics - try: - pex_spice_path = f"{component_name}_pex.spice" - if os.path.exists(pex_spice_path): - os.remove(pex_spice_path) - subprocess.run(["./run_pex.sh", layout_path, component_name], check=True, capture_output=True, text=True) - physical_results["pex"]["status"] = "PEX Complete" - total_res, total_cap = _parse_simple_parasitics(component_name) - physical_results["pex"]["total_resistance_ohms"] = total_res - physical_results["pex"]["total_capacitance_farads"] = total_cap - except subprocess.CalledProcessError as e: - physical_results["pex"]["status"] = f"PEX Error: {e.stderr}" - except FileNotFoundError: - physical_results["pex"]["status"] = "PEX Error: run_pex.sh not found." - except Exception as e: - physical_results["pex"]["status"] = f"PEX Unexpected Error: {e}" - - # Geometric Features - try: - physical_results["geometric"]["raw_area_um2"] = calculate_area(top_level) - sym_h, sym_v = calculate_symmetry_scores(top_level) - physical_results["geometric"]["symmetry_score_horizontal"] = sym_h - physical_results["geometric"]["symmetry_score_vertical"] = sym_v - except Exception as e: - print(f"Warning: Could not calculate geometric features. Error: {e}") - - return physical_results \ No newline at end of file diff --git a/src/glayout/blocks/elementary/FVF/run_pex.sh b/src/glayout/blocks/elementary/FVF/run_pex.sh deleted file mode 100755 index e7a32fd6..00000000 --- a/src/glayout/blocks/elementary/FVF/run_pex.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# Usage: ./run_pex.sh layout.gds layout_cell_name - -GDS_FILE=$1 -LAYOUT_CELL=$2 - -magic -rcfile ./sky130A.magicrc -noconsole -dnull << EOF -gds read $GDS_FILE -flatten $LAYOUT_CELL -load $LAYOUT_CELL -select top cell -extract do local -extract all -ext2sim labels on -ext2sim -extresist tolerance 10 -extresist -ext2spice lvs -ext2spice cthresh 0 -ext2spice extresist on -ext2spice -o ${LAYOUT_CELL}_pex.spice -exit -EOF \ No newline at end of file diff --git a/src/glayout/blocks/elementary/FVF/sky130A.magicrc b/src/glayout/blocks/elementary/FVF/sky130A.magicrc deleted file mode 100644 index 9bb1dbd6..00000000 --- a/src/glayout/blocks/elementary/FVF/sky130A.magicrc +++ /dev/null @@ -1,86 +0,0 @@ -puts stdout "Sourcing design .magicrc for technology sky130A ..." - -# Put grid on 0.005 pitch. This is important, as some commands don't -# rescale the grid automatically (such as lef read?). - -set scalefac [tech lambda] -if {[lindex $scalefac 1] < 2} { - scalegrid 1 2 -} - -# drc off -drc euclidean on -# Change this to a fixed number for repeatable behavior with GDS writes -# e.g., "random seed 12345" -catch {random seed} - -# Turn off the scale option on ext2spice or else it conflicts with the -# scale in the model files. -ext2spice scale off - -# Allow override of PDK path from environment variable PDKPATH -if {[catch {set PDKPATH $env(PDKPATH)}]} { - set PDKPATH $env(PDK_ROOT)/sky130A -} - -# loading technology -tech load $PDKPATH/libs.tech/magic/sky130A.tech - -# load device generator -source $PDKPATH/libs.tech/magic/sky130A.tcl - -# load bind keys (optional) -# source $PDKPATH/libs.tech/magic/sky130A-BindKeys - -# set units to lambda grid -snap lambda - -# set sky130 standard power, ground, and substrate names -set VDD VPWR -set GND VGND -set SUB VSUBS - -# Allow override of type of magic library views used, "mag" or "maglef", -# from environment variable MAGTYPE - -if {[catch {set MAGTYPE $env(MAGTYPE)}]} { - set MAGTYPE mag -} - -# add path to reference cells -if {[file isdir ${PDKPATH}/libs.ref/${MAGTYPE}]} { - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_pr - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_io - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hd - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hdll - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hs - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hvl - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_lp - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_ls - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_ms - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_osu_sc - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_osu_sc_t18 - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_ml_xx_hd - addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_sram_macros -} else { - addpath ${PDKPATH}/libs.ref/sky130_fd_pr/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_io/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hd/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hdll/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hs/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hvl/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_sc_lp/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_sc_ls/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_fd_sc_ms/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_osu_sc/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_osu_sc_t18/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_ml_xx_hd/${MAGTYPE} - addpath ${PDKPATH}/libs.ref/sky130_sram_macros/${MAGTYPE} -} - -# add path to GDS cells - -# add path to IP from catalog. This procedure defined in the PDK script. -catch {magic::query_mylib_ip} -# add path to local IP from user design space. Defined in the PDK script. -catch {magic::query_my_projects} diff --git a/src/glayout/blocks/elementary/FVF/verification.py b/src/glayout/blocks/elementary/FVF/verification.py deleted file mode 100644 index 54cebe35..00000000 --- a/src/glayout/blocks/elementary/FVF/verification.py +++ /dev/null @@ -1,174 +0,0 @@ -# verification.py -import os -import re -import subprocess -import shutil -import tempfile -import sys -from pathlib import Path -from glayout.pdk.sky130_mapped import sky130_mapped_pdk -from gdsfactory.typings import Component - -def parse_drc_report(report_content: str) -> dict: - """ - Parses a Magic DRC report into a machine-readable format. - """ - errors = [] - current_rule = "" - for line in report_content.strip().splitlines(): - stripped_line = line.strip() - if stripped_line == "----------------------------------------": - continue - if re.match(r"^[a-zA-Z]", stripped_line): - current_rule = stripped_line - elif re.match(r"^[0-9]", stripped_line): - errors.append({"rule": current_rule, "details": stripped_line}) - - is_pass = len(errors) == 0 - if not is_pass and re.search(r"count:\s*0\s*$", report_content, re.IGNORECASE): - is_pass = True - - return { - "is_pass": is_pass, - "total_errors": len(errors), - "error_details": errors - } - -def parse_lvs_report(report_content: str) -> dict: - """ - Parses the raw netgen LVS report and returns a summarized, machine-readable format. - Focuses on parsing net and instance mismatches. - """ - summary = { - "is_pass": False, - "conclusion": "LVS failed or report was inconclusive.", - "total_mismatches": 0, - "mismatch_details": { - "nets": "Not found", - "devices": "Not found", - "unmatched_nets_parsed": [], - "unmatched_instances_parsed": [] - } - } - - # Primary check for LVS pass/fail - if "Netlists match" in report_content or "Circuits match uniquely" in report_content: - summary["is_pass"] = True - summary["conclusion"] = "LVS Pass: Netlists match." - elif "Netlist mismatch" in report_content or "Netlists do not match" in report_content: - summary["conclusion"] = "LVS Fail: Netlist mismatch." - - for line in report_content.splitlines(): - line = line.strip() - - # Parse net mismatches - net_mismatch_match = re.search(r"Net:\s*([^\|]+)\s*\|\s*\((no matching net)\)", line) - if net_mismatch_match: - name_left = net_mismatch_match.group(1).strip() - # If name is on the left, it's in layout, missing in schematic - summary["mismatch_details"]["unmatched_nets_parsed"].append({ - "type": "net", - "name": name_left, - "present_in": "layout", - "missing_in": "schematic" - }) - continue - - # Parse instance mismatches - instance_mismatch_match = re.search(r"Instance:\s*([^\|]+)\s*\|\s*\((no matching instance)\)", line) - if instance_mismatch_match: - name_left = instance_mismatch_match.group(1).strip() - # If name is on the left, it's in layout, missing in schematic - summary["mismatch_details"]["unmatched_instances_parsed"].append({ - "type": "instance", - "name": name_left, - "present_in": "layout", - "missing_in": "schematic" - }) - continue - - # Also capture cases where something is present in schematic but missing in layout (right side of '|') - net_mismatch_right_match = re.search(r"\s*\|\s*([^\|]+)\s*\((no matching net)\)", line) - if net_mismatch_right_match: - name_right = net_mismatch_right_match.group(1).strip() - # If name is on the right, it's in schematic, missing in layout - summary["mismatch_details"]["unmatched_nets_parsed"].append({ - "type": "net", - "name": name_right, - "present_in": "schematic", - "missing_in": "layout" - }) - continue - - instance_mismatch_right_match = re.search(r"\s*\|\s*([^\|]+)\s*\((no matching instance)\)", line) - if instance_mismatch_right_match: - name_right = instance_mismatch_right_match.group(1).strip() - # If name is on the right, it's in schematic, missing in layout - summary["mismatch_details"]["unmatched_instances_parsed"].append({ - "type": "instance", - "name": name_right, - "present_in": "schematic", - "missing_in": "layout" - }) - continue - - # Capture summary lines like "Number of devices:" and "Number of nets:" - if "Number of devices:" in line: - summary["mismatch_details"]["devices"] = line.split(":", 1)[1].strip() if ":" in line else line - elif "Number of nets:" in line: - summary["mismatch_details"]["nets"] = line.split(":", 1)[1].strip() if ":" in line else line - - # Calculate total mismatches - summary["total_mismatches"] = len(summary["mismatch_details"]["unmatched_nets_parsed"]) + \ - len(summary["mismatch_details"]["unmatched_instances_parsed"]) - - # If there are any mismatches found, then LVS fails, regardless of "Netlists match" string. - if summary["total_mismatches"] > 0: - summary["is_pass"] = False - if "LVS Pass" in summary["conclusion"]: # If conclusion still says pass, update it - summary["conclusion"] = "LVS Fail: Mismatches found." - - return summary - -def run_verification(layout_path: str, component_name: str, top_level: Component) -> dict: - """ - Runs DRC and LVS checks and returns a structured result dictionary. - """ - verification_results = { - "drc": {"status": "not run", "is_pass": False, "report_path": None, "summary": {}}, - "lvs": {"status": "not run", "is_pass": False, "report_path": None, "summary": {}} - } - - # DRC Check - drc_report_path = os.path.abspath(f"./{component_name}.drc.rpt") - verification_results["drc"]["report_path"] = drc_report_path - try: - if os.path.exists(drc_report_path): - os.remove(drc_report_path) - sky130_mapped_pdk.drc_magic(layout_path, component_name, output_file=drc_report_path) - report_content = "" - if os.path.exists(drc_report_path): - with open(drc_report_path, 'r') as f: - report_content = f.read() - summary = parse_drc_report(report_content) - verification_results["drc"].update({"summary": summary, "is_pass": summary["is_pass"], "status": "pass" if summary["is_pass"] else "fail"}) - except Exception as e: - verification_results["drc"]["status"] = f"error: {e}" - - # LVS Check - lvs_report_path = os.path.abspath(f"./{component_name}.lvs.rpt") - verification_results["lvs"]["report_path"] = lvs_report_path - try: - if os.path.exists(lvs_report_path): - os.remove(lvs_report_path) - sky130_mapped_pdk.lvs_netgen(layout=top_level, design_name=component_name, output_file_path=lvs_report_path) - report_content = "" - if os.path.exists(lvs_report_path): - with open(lvs_report_path, 'r') as report_file: - report_content = report_file.read() - lvs_summary = parse_lvs_report(report_content) - verification_results["lvs"].update({"summary": lvs_summary, "is_pass": lvs_summary["is_pass"], "status": "pass" if lvs_summary["is_pass"] else "fail"}) - except Exception as e: - verification_results["lvs"]["status"] = f"error: {e}" - - return verification_results \ No newline at end of file diff --git a/src/glayout/blocks/elementary/current_mirror/current_mirror.md b/src/glayout/blocks/elementary/current_mirror/current_mirror.md deleted file mode 100644 index fff2c365..00000000 --- a/src/glayout/blocks/elementary/current_mirror/current_mirror.md +++ /dev/null @@ -1,8 +0,0 @@ -# current mirror - -... - -AL: Sep 29 2025 - -Migrated from Arnav's fork of OpenFASOC -- Updated code to make it compatible with latest new gLayout repo \ No newline at end of file diff --git a/src/glayout/blocks/elementary/current_mirror/current_mirror.py b/src/glayout/blocks/elementary/current_mirror/current_mirror.py index 1006de66..054c11a4 100644 --- a/src/glayout/blocks/elementary/current_mirror/current_mirror.py +++ b/src/glayout/blocks/elementary/current_mirror/current_mirror.py @@ -1,54 +1,56 @@ -from glayout import MappedPDK, sky130,gf180 -from glayout.routing import c_route,L_route,straight_route -from glayout.spice.netlist import Netlist from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized +from glayout.pdk.mappedpdk import MappedPDK +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route +from glayout.routing.straight_route import straight_route from glayout.spice.netlist import Netlist +from glayout.pdk.sky130_mapped import sky130_mapped_pdk as sky130 from glayout.primitives.fet import nmos, pmos from glayout.primitives.guardring import tapring -from glayout.util.port_utils import add_ports_perimeter,rename_ports_by_orientation +from glayout.util.port_utils import add_ports_perimeter, rename_ports_by_orientation from gdsfactory.component import Component from gdsfactory.cell import cell from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port from typing import Optional, Union +from glayout.pdk.sky130_mapped import sky130_mapped_pdk from glayout.primitives.via_gen import via_stack from gdsfactory.components import text_freetype, rectangle try: - from evaluator_wrapper import run_evaluation + from glayout.blocks.evaluator_box.evaluator_wrapper import run_evaluation except ImportError: print("Warning: evaluator_wrapper not found. Evaluation will be skipped.") run_evaluation = None -def sky130_add_cm_labels(cm_in: Component) -> Component: +def add_cm_labels(cm_in: Component, + pdk: MappedPDK + ) -> Component: cm_in.unlock() - - # define layers` - met1_pin = (68,16) - met1_label = (68,5) - met2_pin = (69,16) - met2_label = (69,5) + met2_pin = (68,16) + met2_label = (68,5) + # list that will contain all port/comp info move_info = list() # create labels and append to info list # vss - vsslabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vsslabel.add_label(text="VSS",layer=met1_label) + vsslabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vsslabel.add_label(text="VSS",layer=pdk.get_glayer("met2_label")) move_info.append((vsslabel,cm_in.ports["fet_A_source_E"],None)) # vref - vreflabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vreflabel.add_label(text="VREF",layer=met1_label) + vreflabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vreflabel.add_label(text="VREF",layer=pdk.get_glayer("met2_label")) move_info.append((vreflabel,cm_in.ports["fet_A_drain_N"],None)) # vcopy - vcopylabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vcopylabel.add_label(text="VCOPY",layer=met1_label) + vcopylabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vcopylabel.add_label(text="VCOPY",layer=pdk.get_glayer("met2_label")) move_info.append((vcopylabel,cm_in.ports["fet_B_drain_N"],None)) # VB - vblabel = rectangle(layer=met1_pin,size=(0.5,0.5),centered=True).copy() - vblabel.add_label(text="VB",layer=met1_label) + vblabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.5,0.5),centered=True).copy() + vblabel.add_label(text="VB",layer=pdk.get_glayer("met2_label")) move_info.append((vblabel,cm_in.ports["welltie_S_top_met_S"], None)) # move everything to position @@ -63,7 +65,7 @@ def current_mirror_netlist( width: float, length: float, multipliers: int, - with_dummy: Optional[bool] = False, + with_dummy: bool = True, n_or_p_fet: Optional[str] = 'nfet', subckt_only: Optional[bool] = False ) -> Netlist: @@ -97,7 +99,7 @@ def current_mirror_netlist( ) -@cell +#@cell def current_mirror( pdk: MappedPDK, numcols: int = 3, @@ -158,10 +160,10 @@ def current_mirror( if device in ['pmos','pfet']: tap_layer = "n+s/d" tap_sep = max( - pdk.util_max_metal_seperation(), - pdk.get_grule("active_diff", "active_tap")["min_separation"], + float(pdk.util_max_metal_seperation()), + float(pdk.get_grule("active_diff", "active_tap")["min_separation"]), ) - tap_sep += pdk.get_grule(tap_layer, "active_tap")["min_enclosure"] + tap_sep += float(pdk.get_grule(tap_layer, "active_tap")["min_enclosure"]) tap_encloses = ( 2 * (tap_sep + interdigitized_fets.xmax), 2 * (tap_sep + interdigitized_fets.ymax), @@ -210,121 +212,12 @@ def current_mirror( ) return top_level - - -def sky130_add_current_mirror_labels(current_mirror_in: Component) -> Component: - """ - Add labels to current mirror component for simulation and testing - """ - current_mirror_in.unlock() - # define layers - met1_pin = (68,16) - met1_label = (68,5) - met2_pin = (69,16) - met2_label = (69,5) - # list that will contain all port/comp info - move_info = list() - - # Reference voltage (drain of reference transistor) - vref_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - vref_label.add_label(text="VREF", layer=met1_label) - - # Copy current output (drain of mirror transistor) - vcopy_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - vcopy_label.add_label(text="VCOPY", layer=met1_label) - - # Ground/VSS (source connections) - vss_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - vss_label.add_label(text="VSS", layer=met1_label) - - # Bulk/VB (bulk/body connections) - vb_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - vb_label.add_label(text="VB", layer=met1_label) - - # Try to find appropriate ports and add labels - try: - # Look for drain ports for VREF and VCOPY - ref_drain_ports = [p for p in current_mirror_in.ports.keys() if 'A_drain' in p and 'met' in p] - copy_drain_ports = [p for p in current_mirror_in.ports.keys() if 'B_drain' in p and 'met' in p] - source_ports = [p for p in current_mirror_in.ports.keys() if 'source' in p and 'met' in p] - bulk_ports = [p for p in current_mirror_in.ports.keys() if ('tie' in p or 'well' in p) and 'met' in p] - - if ref_drain_ports: - move_info.append((vref_label, current_mirror_in.ports[ref_drain_ports[0]], None)) - if copy_drain_ports: - move_info.append((vcopy_label, current_mirror_in.ports[copy_drain_ports[0]], None)) - if source_ports: - move_info.append((vss_label, current_mirror_in.ports[source_ports[0]], None)) - if bulk_ports: - move_info.append((vb_label, current_mirror_in.ports[bulk_ports[0]], None)) - - except (KeyError, IndexError): - # Fallback - just add labels at component center - print("Warning: Could not find specific ports for labels, using fallback positioning") - move_info = [ - (vref_label, None, None), - (vcopy_label, None, None), - (vss_label, None, None), - (vb_label, None, None) - ] - - # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - if prt is not None: - compref = align_comp_to_port(comp, prt, alignment=alignment) - else: - compref = comp - current_mirror_in.add(compref) - - return current_mirror_in.flatten() - - -# Create and evaluate a current mirror instance -if __name__ == "__main__": - # OLD EVAL CODE - # comp = current_mirror(sky130) - # # comp.pprint_ports() - # comp = add_cm_labels(comp,sky130) - # comp.name = "CM" - # comp.show() - # #print(comp.info['netlist'].generate_netlist()) - # print("...Running DRC...") - # drc_result = sky130.drc_magic(comp, "CM") - # ## Klayout DRC - # #drc_result = sky130.drc(comp)\n - - # time.sleep(5) - - # print("...Running LVS...") - # lvs_res=sky130.lvs_netgen(comp, "CM") - # #print("...Saving GDS...") - # #comp.write_gds('out_CMirror.gds') - - # NEW EVAL CODE - # Create current mirror with labels - cm = sky130_add_current_mirror_labels( - current_mirror( - pdk=sky130, - numcols=3, - device='nfet', - width=3, - length=1, - with_dummy=True, - with_tie=True - ) - ) - - # Show the layout - cm.show() - cm.name = "current_mirror" - - # Write GDS file - cm_gds = cm.write_gds("current_mirror.gds") - # Run evaluation if available - if run_evaluation is not None: - result = run_evaluation("current_mirror.gds", cm.name, cm) - print(result) - else: - print("Evaluation skipped - evaluator_wrapper not available") +if __name__=="__main__": + current_mirror = add_cm_labels(current_mirror(sky130_mapped_pdk, device='pfet'),sky130_mapped_pdk) + current_mirror.show() + current_mirror.name = "CMIRROR" + #magic_drc_result = sky130_mapped_pdk.drc_magic(current_mirror, current_mirror.name) + #netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(current_mirror, current_mirror.name) + current_mirror_gds = current_mirror.write_gds("current_mirror.gds") + res = run_evaluation("current_mirror.gds", current_mirror.name, current_mirror) \ No newline at end of file diff --git a/src/glayout/blocks/elementary/diff_pair/diff_pair.md b/src/glayout/blocks/elementary/diff_pair/diff_pair.md deleted file mode 100644 index fc8585cb..00000000 --- a/src/glayout/blocks/elementary/diff_pair/diff_pair.md +++ /dev/null @@ -1,10 +0,0 @@ -# differential pair - -... - -AL: Sep 29 2025 - -Migrated from Arnav's fork of OpenFASOC -- Updated code to make it compatible with latest new gLayout repo -- There're some issue with the add_df_labels function -- The \_\_main\_\_ code need to be updated. diff --git a/src/glayout/blocks/elementary/diff_pair/diff_pair.py b/src/glayout/blocks/elementary/diff_pair/diff_pair.py index 26b63acd..759e5695 100644 --- a/src/glayout/blocks/elementary/diff_pair/diff_pair.py +++ b/src/glayout/blocks/elementary/diff_pair/diff_pair.py @@ -1,7 +1,4 @@ from typing import Optional, Union -from glayout import MappedPDK, sky130,gf180 -from glayout.spice.netlist import Netlist -from glayout.routing import c_route,L_route,straight_route from gdsfactory.cell import cell from gdsfactory.component import Component, copy @@ -23,192 +20,80 @@ from glayout.primitives.fet import nmos, pmos from glayout.primitives.guardring import tapring from glayout.primitives.via_gen import via_stack +from glayout.routing.c_route import c_route from glayout.routing.smart_route import smart_route +from glayout.routing.straight_route import straight_route from glayout.spice import Netlist from glayout.pdk.sky130_mapped import sky130_mapped_pdk from gdsfactory.components import text_freetype try: - from evaluator_wrapper import run_evaluation + from glayout.blocks.evaluator_box.evaluator_wrapper import run_evaluation except ImportError: print("Warning: evaluator_wrapper not found. Evaluation will be skipped.") run_evaluation = None -def sky130_add_df_labels(df_in: Component) -> Component: +def add_df_labels(df_in: Component, + pdk: MappedPDK + ) -> Component: - df_in.unlock() - - # define layers` - met1_pin = (68,16) - met1_label = (68,5) - li1_pin = (67,16) - li1_label = (67,5) + df_in.unlock() + met1_pin = (67,16) + met1_label = (67,5) + met2_pin = (68,16) + met2_label = (68,5) # list that will contain all port/comp info - move_info = list() + move_info = list() # create labels and append to info list # vtail - vtaillabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vtaillabel.add_label(text="VTAIL",layer=met1_label) - move_info.append((vtaillabel,df_in.ports["bl_multiplier_0_source_S"],None)) + vtaillabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vtaillabel.add_label(text="VTAIL",layer=pdk.get_glayer("met2_label")) + move_info.append((vtaillabel,df_in.ports["bl_multiplier_0_source_S"],None)) # vdd1 - vdd1label = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vdd1label.add_label(text="VDD1",layer=met1_label) - move_info.append((vdd1label,df_in.ports["tl_multiplier_0_drain_N"],None)) + vdd1label = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vdd1label.add_label(text="VDD1",layer=pdk.get_glayer("met2_label")) + move_info.append((vdd1label,df_in.ports["tl_multiplier_0_drain_N"],None)) # vdd2 - vdd2label = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vdd2label.add_label(text="VDD2",layer=met1_label) - move_info.append((vdd2label,df_in.ports["tr_multiplier_0_drain_N"],None)) + vdd2label = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vdd2label.add_label(text="VDD2",layer=pdk.get_glayer("met2_label")) + move_info.append((vdd2label,df_in.ports["tr_multiplier_0_drain_N"],None)) # VB - vblabel = rectangle(layer=li1_pin,size=(0.5,0.5),centered=True).copy() - vblabel.add_label(text="B",layer=li1_label) - move_info.append((vblabel,df_in.ports["tap_N_top_met_S"], None)) + vblabel = rectangle(layer=pdk.get_glayer("met1_pin"),size=(0.5,0.5),centered=True).copy() + vblabel.add_label(text="B",layer=pdk.get_glayer("met1_label")) + move_info.append((vblabel,df_in.ports["tap_N_top_met_S"], None)) # VP - vplabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vplabel.add_label(text="VP",layer=met1_label) - move_info.append((vplabel,df_in.ports["br_multiplier_0_gate_S"], None)) + vplabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vplabel.add_label(text="VP",layer=pdk.get_glayer("met2_label")) + move_info.append((vplabel,df_in.ports["br_multiplier_0_gate_S"], None)) # VN - vnlabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy() - vnlabel.add_label(text="VN",layer=met1_label) - move_info.append((vnlabel,df_in.ports["bl_multiplier_0_gate_S"], None)) + vnlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy() + vnlabel.add_label(text="VN",layer=pdk.get_glayer("met2_label")) + move_info.append((vnlabel,df_in.ports["bl_multiplier_0_gate_S"], None)) # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - compref = align_comp_to_port(comp, prt, alignment=alignment) - df_in.add(compref) - return df_in.flatten() + for comp, prt, alignment in move_info: + alignment = ('c','b') if alignment is None else alignment + compref = align_comp_to_port(comp, prt, alignment=alignment) + df_in.add(compref) + return df_in.flatten() def diff_pair_netlist(fetL: Component, fetR: Component) -> Netlist: diff_pair_netlist = Netlist(circuit_name='DIFF_PAIR', nodes=['VP', 'VN', 'VDD1', 'VDD2', 'VTAIL', 'B']) - - # Handle fetL netlist - reconstruct if it's a string - fetL_netlist = fetL.info['netlist'] - if isinstance(fetL_netlist, str): - if 'netlist_data' in fetL.info: - data = fetL.info['netlist_data'] - fetL_netlist = Netlist(circuit_name=data['circuit_name'], nodes=data['nodes']) - fetL_netlist.source_netlist = data['source_netlist'] - if 'parameters' in data: - fetL_netlist.parameters = data['parameters'] - else: - raise ValueError("No netlist_data found for string netlist in fetL component.info") - - # Handle fetR netlist - reconstruct if it's a string - fetR_netlist = fetR.info['netlist'] - if isinstance(fetR_netlist, str): - if 'netlist_data' in fetR.info: - data = fetR.info['netlist_data'] - fetR_netlist = Netlist(circuit_name=data['circuit_name'], nodes=data['nodes']) - fetR_netlist.source_netlist = data['source_netlist'] - if 'parameters' in data: - fetR_netlist.parameters = data['parameters'] - else: - raise ValueError("No netlist_data found for string netlist in fetR component.info") - diff_pair_netlist.connect_netlist( - fetL_netlist, + fetL.info['netlist'], [('D', 'VDD1'), ('G', 'VP'), ('S', 'VTAIL'), ('B', 'B')] ) diff_pair_netlist.connect_netlist( - fetR_netlist, + fetR.info['netlist'], [('D', 'VDD2'), ('G', 'VN'), ('S', 'VTAIL'), ('B', 'B')] ) return diff_pair_netlist -def sky130_add_diff_pair_labels(diff_pair_in: Component) -> Component: - """ - Add labels to differential pair component for simulation and testing - """ - diff_pair_in.unlock() - # define layers - met1_pin = (68,16) - met1_label = (68,5) - met2_pin = (69,16) - met2_label = (69,5) - # list that will contain all port/comp info - move_info = list() - - # Positive input (VP) - vp_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - vp_label.add_label(text="VP", layer=met1_label) - - # Negative input (VN) - vn_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - vn_label.add_label(text="VN", layer=met1_label) - - # Positive output drain (VDD1) - vdd1_label = rectangle(layer=met2_pin, size=(0.5,0.5), centered=True).copy() - vdd1_label.add_label(text="VDD1", layer=met2_label) - - # Negative output drain (VDD2) - vdd2_label = rectangle(layer=met2_pin, size=(0.5,0.5), centered=True).copy() - vdd2_label.add_label(text="VDD2", layer=met2_label) - - # Tail current (VTAIL) - vtail_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - vtail_label.add_label(text="VTAIL", layer=met1_label) - - # Bulk/Body (B) - b_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy() - b_label.add_label(text="B", layer=met1_label) - - # Try to find appropriate ports and add labels - try: - # Look for gate ports for VP and VN - plus_gate_ports = [p for p in diff_pair_in.ports.keys() if 'PLUSgate' in p and 'met' in p] - minus_gate_ports = [p for p in diff_pair_in.ports.keys() if 'MINUSgate' in p and 'met' in p] - - # Look for drain ports for VDD1 and VDD2 - drain_ports = [p for p in diff_pair_in.ports.keys() if 'drain_route' in p and 'met' in p] - - # Look for source ports for VTAIL - source_ports = [p for p in diff_pair_in.ports.keys() if 'source_route' in p and 'met' in p] - - # Look for bulk/tie ports - bulk_ports = [p for p in diff_pair_in.ports.keys() if ('tie' in p or 'well' in p or 'tap' in p) and 'met' in p] - - if plus_gate_ports: - move_info.append((vp_label, diff_pair_in.ports[plus_gate_ports[0]], None)) - if minus_gate_ports: - move_info.append((vn_label, diff_pair_in.ports[minus_gate_ports[0]], None)) - if len(drain_ports) >= 2: - move_info.append((vdd1_label, diff_pair_in.ports[drain_ports[0]], None)) - move_info.append((vdd2_label, diff_pair_in.ports[drain_ports[1]], None)) - elif len(drain_ports) == 1: - move_info.append((vdd1_label, diff_pair_in.ports[drain_ports[0]], None)) - if source_ports: - move_info.append((vtail_label, diff_pair_in.ports[source_ports[0]], None)) - if bulk_ports: - move_info.append((b_label, diff_pair_in.ports[bulk_ports[0]], None)) - - except (KeyError, IndexError): - # Fallback - just add labels at component center - print("Warning: Could not find specific ports for labels, using fallback positioning") - move_info = [ - (vp_label, None, None), - (vn_label, None, None), - (vdd1_label, None, None), - (vdd2_label, None, None), - (vtail_label, None, None), - (b_label, None, None) - ] - - # move everything to position - for comp, prt, alignment in move_info: - alignment = ('c','b') if alignment is None else alignment - if prt is not None: - compref = align_comp_to_port(comp, prt, alignment=alignment) - else: - compref = comp - diff_pair_in.add(compref) - - return diff_pair_in.flatten() - @cell def diff_pair( pdk: MappedPDK, @@ -257,10 +142,10 @@ def diff_pair( a_topl = (diffpair << fetL).movey(fetL.ymax+min_spacing_y/2+0.5).movex(0-fetL.xmax-min_spacing_x/2) b_topr = (diffpair << fetR).movey(fetR.ymax+min_spacing_y/2+0.5).movex(fetL.xmax+min_spacing_x/2) a_botr = (diffpair << fetR) - a_botr.mirror_y() + a_botr = a_botr.mirror_y() a_botr.movey(0-0.5-fetL.ymax-min_spacing_y/2).movex(fetL.xmax+min_spacing_x/2) b_botl = (diffpair << fetL) - b_botl.mirror_y() + b_botl = b_botl.mirror_y() b_botl.movey(0-0.5-fetR.ymax-min_spacing_y/2).movex(0-fetL.xmax-min_spacing_x/2) # if substrate tap place substrate tap if substrate_tap: @@ -286,8 +171,8 @@ def diff_pair( diffpair << route_quad(a_topl.ports["multiplier_0_source_E"], b_topr.ports["multiplier_0_source_W"], layer=pdk.get_glayer("met2")) diffpair << route_quad(b_botl.ports["multiplier_0_source_E"], a_botr.ports["multiplier_0_source_W"], layer=pdk.get_glayer("met2")) sextension = b_topr.ports["well_E"].center[0] - b_topr.ports["multiplier_0_source_E"].center[0] - source_routeE = diffpair << c_route(pdk, b_topr.ports["multiplier_0_source_E"], a_botr.ports["multiplier_0_source_E"],extension=sextension) - source_routeW = diffpair << c_route(pdk, a_topl.ports["multiplier_0_source_W"], b_botl.ports["multiplier_0_source_W"],extension=sextension) + source_routeE = diffpair << c_route(pdk, b_topr.ports["multiplier_0_source_E"], a_botr.ports["multiplier_0_source_E"],extension=sextension, viaoffset=False) + source_routeW = diffpair << c_route(pdk, a_topl.ports["multiplier_0_source_W"], b_botl.ports["multiplier_0_source_W"],extension=sextension, viaoffset=False) # route drains # place via at the drain drain_br_via = diffpair << viam2m3 @@ -361,50 +246,12 @@ def diff_pair_generic( diffpair << smart_route(pdk,diffpair.ports["A_source_E"],diffpair.ports["B_source_E"],diffpair, diffpair) return diffpair - -# Create and evaluate a differential pair instance -if __name__ == "__main__": - # this is the old eval code - - # Create differential pair with labels - comp =diff_pair(sky130) - # comp.pprint_ports() - comp=sky130_add_df_labels(comp) - comp.name = "DiffPair" - comp.show() - #print(comp.info['netlist'].generate_netlist()) - print("...Running DRC...") - drc_result = sky130.drc_magic(comp, "DiffPair") - ## Klayout DRC - #drc_result = sky130.drc(comp)\n - time.sleep(5) - print("...Running LVS...") - lvs_res=sky130.lvs_netgen(comp, "DiffPair") - #print("...Saving GDS...") - #comp.write_gds('out_Diffpair.gds') - - # This is the new eval code - dp = sky130_add_diff_pair_labels( - diff_pair( - pdk=sky130, - width=3, - fingers=4, - length=None, - n_or_p_fet=True, - plus_minus_seperation=0, - rmult=1, - dummy=True, - substrate_tap=True - ) - ) - # Show the layout - dp.show() - dp.name = "diff_pair" - # Write GDS file - dp_gds = dp.write_gds("diff_pair.gds") - # Run evaluation if available - if run_evaluation is not None: - result = run_evaluation("diff_pair.gds", dp.name, dp) - print(result) - else: - print("Evaluation skipped - evaluator_wrapper not available") +if __name__=="__main__": + diff_pair = add_df_labels(diff_pair(sky130_mapped_pdk),sky130_mapped_pdk) + #diff_pair = diff_pair(sky130_mapped_pdk) + diff_pair.show() + diff_pair.name = "DIFF_PAIR" + #magic_drc_result = sky130_mapped_pdk.drc_magic(diff_pair, diff_pair.name) + #netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(diff_pair, diff_pair.name) + diff_pair_gds = diff_pair.write_gds("diff_pair.gds") + res = run_evaluation("diff_pair.gds", diff_pair.name, diff_pair) \ No newline at end of file diff --git a/src/glayout/blocks/elementary/transmission_gate/transmission_gate.md b/src/glayout/blocks/elementary/transmission_gate/transmission_gate.md deleted file mode 100644 index fff2c365..00000000 --- a/src/glayout/blocks/elementary/transmission_gate/transmission_gate.md +++ /dev/null @@ -1,8 +0,0 @@ -# current mirror - -... - -AL: Sep 29 2025 - -Migrated from Arnav's fork of OpenFASOC -- Updated code to make it compatible with latest new gLayout repo \ No newline at end of file diff --git a/src/glayout/blocks/elementary/transmission_gate/transmission_gate.py b/src/glayout/blocks/elementary/transmission_gate/transmission_gate.py index 895cb5c0..c10f6028 100644 --- a/src/glayout/blocks/elementary/transmission_gate/transmission_gate.py +++ b/src/glayout/blocks/elementary/transmission_gate/transmission_gate.py @@ -15,7 +15,7 @@ from glayout.primitives.via_gen import via_stack from gdsfactory.components import text_freetype, rectangle try: - from evaluator_wrapper import run_evaluation + from glayout.blocks.evaluator_box.evaluator_wrapper import run_evaluation except ImportError: print("Warning: evaluator_wrapper not found. Evaluation will be skipped.") run_evaluation = None @@ -204,7 +204,9 @@ def transmission_gate( # Store netlist as string to avoid gymnasium info dict type restrictions # Compatible with both gdsfactory 7.7.0 and 7.16.0+ strict Pydantic validation netlist_obj = tg_netlist(nfet, pfet) - component.info['netlist'] = str(netlist_obj) + component.info['netlist'] = netlist_obj.generate_netlist() + # Store the Netlist object for hierarchical netlist building + component.info['netlist_obj'] = netlist_obj # Store serialized netlist data for reconstruction if needed component.info['netlist_data'] = { 'circuit_name': netlist_obj.circuit_name, diff --git a/src/glayout/blocks/evaluator_box/evaluator_box.md b/src/glayout/blocks/evaluator_box/evaluator_box.md deleted file mode 100644 index 2cfbd019..00000000 --- a/src/glayout/blocks/evaluator_box/evaluator_box.md +++ /dev/null @@ -1,7 +0,0 @@ -# Evaluator Box - -AL: Sep 29 2025 - -Migrated from Arnav's fork of OpenFASOC -- Updated code to make it compatible with latest new gLayout repo -- Not tested yet \ No newline at end of file diff --git a/src/glayout/blocks/evaluator_box/evaluator_wrapper.py b/src/glayout/blocks/evaluator_box/evaluator_wrapper.py index f8897ddf..7ff0863a 100644 --- a/src/glayout/blocks/evaluator_box/evaluator_wrapper.py +++ b/src/glayout/blocks/evaluator_box/evaluator_wrapper.py @@ -3,12 +3,19 @@ import os import json import logging +import shutil from datetime import datetime from pathlib import Path from gdsfactory.typings import Component -from verification import run_verification -from physical_features import run_physical_feature_extraction +# Make run_pex.sh executable if it exists +_evaluator_box_dir = Path(__file__).parent +_run_pex_script = _evaluator_box_dir / "run_pex.sh" +if _run_pex_script.exists(): + os.chmod(_run_pex_script, 0o755) + +from glayout.blocks.evaluator_box.verification import run_verification +from glayout.blocks.evaluator_box.physical_features import run_physical_feature_extraction def get_next_filename(base_name="evaluation", extension=".json"): """ @@ -28,22 +35,28 @@ def run_evaluation(layout_path: str, component_name: str, top_level: Component) """ print(f"--- Starting Comprehensive Evaluation for {component_name} ---") - # Deletes known intermediate and report files for a given component to ensure a clean run. + # Deletes known intermediate and report files/directories for a given component to ensure a clean run. print(f"Cleaning up intermediate files for component '{component_name}'...") - files_to_delete = [ + items_to_delete = [ f"{component_name}.res.ext", f"{component_name}.lvs.rpt", f"{component_name}_lvs.rpt", + f"{component_name}.drc.rpt", + f"{component_name}_drc_out", # New DRC output directory + f"{component_name}_lvs_out", # New LVS output directory f"{component_name}.nodes", f"{component_name}.sim", f"{component_name}.pex.spice", f"{component_name}_pex.spice" ] - for f_path in files_to_delete: + for f_path in items_to_delete: try: - if os.path.exists(f_path): + if os.path.isdir(f_path): + shutil.rmtree(f_path) + print(f" - Deleted directory: {f_path}") + elif os.path.exists(f_path): os.remove(f_path) print(f" - Deleted: {f_path}") except OSError as e: diff --git a/src/glayout/blocks/evaluator_box/physical_features.py b/src/glayout/blocks/evaluator_box/physical_features.py index 5e930b1c..2ce54377 100644 --- a/src/glayout/blocks/evaluator_box/physical_features.py +++ b/src/glayout/blocks/evaluator_box/physical_features.py @@ -7,6 +7,10 @@ from gdsfactory.typings import Component from gdsfactory.geometry.boolean import boolean +# Get the path to run_pex.sh in the evaluator_box directory +_EVALUATOR_BOX_DIR = Path(__file__).parent +_RUN_PEX_SCRIPT = _EVALUATOR_BOX_DIR / "run_pex.sh" + def calculate_area(component: Component) -> float: """Calculates the area of a gdsfactory Component.""" return float(component.area()) @@ -90,17 +94,20 @@ def run_physical_feature_extraction(layout_path: str, component_name: str, top_l pex_spice_path = f"{component_name}_pex.spice" if os.path.exists(pex_spice_path): os.remove(pex_spice_path) - # Invoke via explicit "bash" to avoid execute-bit issues on some systems - # If run_pex.sh lacks +x permission, this still works reliably. - subprocess.run(["bash", "run_pex.sh", layout_path, component_name], check=True, capture_output=True, text=True) + # Use the run_pex.sh from evaluator_box directory, not current directory + if not _RUN_PEX_SCRIPT.exists(): + raise FileNotFoundError(f"run_pex.sh not found at {_RUN_PEX_SCRIPT}") + # Make sure it's executable + os.chmod(_RUN_PEX_SCRIPT, 0o755) + subprocess.run([str(_RUN_PEX_SCRIPT), layout_path, component_name], check=True, capture_output=True, text=True) physical_results["pex"]["status"] = "PEX Complete" total_res, total_cap = _parse_simple_parasitics(component_name) physical_results["pex"]["total_resistance_ohms"] = total_res physical_results["pex"]["total_capacitance_farads"] = total_cap except subprocess.CalledProcessError as e: physical_results["pex"]["status"] = f"PEX Error: {e.stderr}" - except FileNotFoundError: - physical_results["pex"]["status"] = "PEX Error: run_pex.sh not found." + except FileNotFoundError as e: + physical_results["pex"]["status"] = f"PEX Error: {e}" except Exception as e: physical_results["pex"]["status"] = f"PEX Unexpected Error: {e}" @@ -113,4 +120,4 @@ def run_physical_feature_extraction(layout_path: str, component_name: str, top_l except Exception as e: print(f"Warning: Could not calculate geometric features. Error: {e}") - return physical_results \ No newline at end of file + return physical_results \ No newline at end of file diff --git a/src/glayout/blocks/evaluator_box/run_pex.sh b/src/glayout/blocks/evaluator_box/run_pex.sh index e7a32fd6..745e40d7 100755 --- a/src/glayout/blocks/evaluator_box/run_pex.sh +++ b/src/glayout/blocks/evaluator_box/run_pex.sh @@ -1,11 +1,21 @@ #!/bin/bash # Usage: ./run_pex.sh layout.gds layout_cell_name +# Requires PDK_ROOT environment variable to be set GDS_FILE=$1 LAYOUT_CELL=$2 -magic -rcfile ./sky130A.magicrc -noconsole -dnull << EOF +# Use PDK_ROOT to find the magicrc file +MAGICRC="${PDK_ROOT}/sky130A/libs.tech/magic/sky130A.magicrc" + +if [ ! -f "$MAGICRC" ]; then + echo "Error: Cannot find magicrc at $MAGICRC" + echo "Make sure PDK_ROOT is set correctly (current: $PDK_ROOT)" + exit 1 +fi + +magic -rcfile "$MAGICRC" -noconsole -dnull << EOF gds read $GDS_FILE flatten $LAYOUT_CELL load $LAYOUT_CELL diff --git a/src/glayout/blocks/evaluator_box/verification.py b/src/glayout/blocks/evaluator_box/verification.py index e423bd55..a1a14553 100644 --- a/src/glayout/blocks/evaluator_box/verification.py +++ b/src/glayout/blocks/evaluator_box/verification.py @@ -133,6 +133,9 @@ def parse_lvs_report(report_content: str) -> dict: def run_verification(layout_path: str, component_name: str, top_level: Component) -> dict: """ Runs DRC and LVS checks and returns a structured result dictionary. + Note: PDK functions create directory structures: + - DRC: {output_dir}/drc/{design_name}/{design_name}.rpt + - LVS: {output_dir}/lvs/{design_name}/{design_name}_lvs.rpt """ verification_results = { "drc": {"status": "not run", "is_pass": False, "report_path": None, "summary": {}}, @@ -140,15 +143,18 @@ def run_verification(layout_path: str, component_name: str, top_level: Component } # DRC Check - drc_report_path = os.path.abspath(f"./{component_name}.drc.rpt") - verification_results["drc"]["report_path"] = drc_report_path + # PDK creates: {drc_output_dir}/drc/{component_name}/{component_name}.rpt + drc_output_dir = os.path.abspath(f"./{component_name}_drc_out") + drc_actual_report = os.path.join(drc_output_dir, "drc", component_name, f"{component_name}.rpt") + verification_results["drc"]["report_path"] = drc_actual_report try: - if os.path.exists(drc_report_path): - os.remove(drc_report_path) - sky130.drc_magic(layout_path, component_name, output_file=drc_report_path) + # Clean up existing directory if present + if os.path.exists(drc_output_dir): + shutil.rmtree(drc_output_dir) + sky130.drc_magic(layout_path, component_name, output_file=drc_output_dir) report_content = "" - if os.path.exists(drc_report_path): - with open(drc_report_path, 'r') as f: + if os.path.exists(drc_actual_report): + with open(drc_actual_report, 'r') as f: report_content = f.read() summary = parse_drc_report(report_content) verification_results["drc"].update({"summary": summary, "is_pass": summary["is_pass"], "status": "pass" if summary["is_pass"] else "fail"}) @@ -156,15 +162,18 @@ def run_verification(layout_path: str, component_name: str, top_level: Component verification_results["drc"]["status"] = f"error: {e}" # LVS Check - lvs_report_path = os.path.abspath(f"./{component_name}.lvs.rpt") - verification_results["lvs"]["report_path"] = lvs_report_path + # PDK creates: {lvs_output_dir}/lvs/{component_name}/{component_name}_lvs.rpt + lvs_output_dir = os.path.abspath(f"./{component_name}_lvs_out") + lvs_actual_report = os.path.join(lvs_output_dir, "lvs", component_name, f"{component_name}_lvs.rpt") + verification_results["lvs"]["report_path"] = lvs_actual_report try: - if os.path.exists(lvs_report_path): - os.remove(lvs_report_path) - sky130.lvs_netgen(layout=top_level, design_name=component_name, output_file_path=lvs_report_path) + # Clean up existing directory if present + if os.path.exists(lvs_output_dir): + shutil.rmtree(lvs_output_dir) + sky130.lvs_netgen(layout=top_level, design_name=component_name, output_file_path=lvs_output_dir) report_content = "" - if os.path.exists(lvs_report_path): - with open(lvs_report_path, 'r') as report_file: + if os.path.exists(lvs_actual_report): + with open(lvs_actual_report, 'r') as report_file: report_content = report_file.read() lvs_summary = parse_lvs_report(report_content) verification_results["lvs"].update({"summary": lvs_summary, "is_pass": lvs_summary["is_pass"], "status": "pass" if lvs_summary["is_pass"] else "fail"}) diff --git a/src/glayout/pdk/mappedpdk.py b/src/glayout/pdk/mappedpdk.py index d6193ff3..dd8b9732 100644 --- a/src/glayout/pdk/mappedpdk.py +++ b/src/glayout/pdk/mappedpdk.py @@ -293,7 +293,6 @@ class MappedPDK(Pdk): # friendly way to implement a graph grules: dict[StrictStr, dict[StrictStr, Optional[dict[StrictStr, Any]]]] pdk_files: dict[StrictStr, Union[PathType, None]] - valid_bjt_sizes: dict[StrictStr, list[tuple[float,float]]] @validator("models") @@ -764,7 +763,14 @@ def write_spice(input_cdl, output_spice, lvs_schematic_ref_file): layout.write_gds(str(gds_path)) if netlist is None: - netlist = layout.info['netlist'].generate_netlist() + # Handle both string netlists and Netlist objects + netlist_info = layout.info['netlist'] + if isinstance(netlist_info, str): + # Already a string, use directly + netlist = netlist_info + else: + # Netlist object, call generate_netlist() + netlist = netlist_info.generate_netlist() with open(str(netlist_from_comp), 'w') as f: f.write(netlist) else: