diff --git a/src/glayout/blocks/ATLAS/current_mirror.py b/src/glayout/blocks/ATLAS/current_mirror.py index 3d7bf11f..6097dcb7 100644 --- a/src/glayout/blocks/ATLAS/current_mirror.py +++ b/src/glayout/blocks/ATLAS/current_mirror.py @@ -1,19 +1,19 @@ -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 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 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 glayout.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 glayout.pdk.sky130_mapped import sky130_mapped_pdk +from glayout.primitives.via_gen import via_stack from gdsfactory.components import text_freetype, rectangle try: 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 index 116a58cd..8c3221a7 100644 --- a/src/glayout/blocks/ATLAS/diff_pair.py +++ b/src/glayout/blocks/ATLAS/diff_pair.py @@ -5,9 +5,9 @@ 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 ( +from glayout.pdk.mappedpdk import MappedPDK +from glayout.util.comp_utils import align_comp_to_port, evaluate_bbox, movex, movey +from glayout.util.port_utils import ( add_ports_perimeter, get_orientation, print_ports, @@ -15,16 +15,16 @@ 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 glayout.util.snap_to_grid import component_snap_to_grid +from glayout.placement.common_centroid_ab_ba import common_centroid_ab_ba +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 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 index 106a932d..8ef53c46 100644 --- a/src/glayout/blocks/ATLAS/fvf.py +++ b/src/glayout/blocks/ATLAS/fvf.py @@ -1,25 +1,25 @@ -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 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 glayout.primitives.fet import nmos, pmos, multiplier +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 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 + from glayout.spice.netlist import Netlist # Try to get stored object first (for older gdsfactory versions) if 'netlist_obj' in component.info: @@ -183,10 +183,12 @@ def flipped_voltage_follower( 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 + # 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'] = str(netlist_obj) + 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, diff --git a/src/glayout/blocks/ATLAS/lvcm.py b/src/glayout/blocks/ATLAS/lvcm.py index 9e85ec6b..9bfb6091 100644 --- a/src/glayout/blocks/ATLAS/lvcm.py +++ b/src/glayout/blocks/ATLAS/lvcm.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.LHS.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 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 from evaluator_wrapper import run_evaluation @@ -67,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')]) @@ -184,7 +185,8 @@ 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 diff --git a/src/glayout/blocks/ATLAS/opamp.py b/src/glayout/blocks/ATLAS/opamp.py index d5b25690..17b54962 100644 --- a/src/glayout/blocks/ATLAS/opamp.py +++ b/src/glayout/blocks/ATLAS/opamp.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 index 3e42e7dc..5285c484 100644 --- a/src/glayout/blocks/ATLAS/transmission_gate.py +++ b/src/glayout/blocks/ATLAS/transmission_gate.py @@ -1,19 +1,19 @@ -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 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 glayout.primitives.fet import nmos, pmos, multiplier +from glayout.util.comp_utils import evaluate_bbox, prec_center, align_comp_to_port, movex, movey +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 try: from evaluator_wrapper import run_evaluation # pyright: ignore[reportMissingImports] @@ -86,30 +86,21 @@ def get_component_netlist(component) -> Netlist: # Fallback: return empty netlist return Netlist() -def tg_netlist(nfet_comp, pfet_comp) -> str: - """Generate SPICE netlist string for transmission gate - gymnasium compatible""" +def tg_netlist(nfet: Component, pfet: Component) -> Netlist: + """Generate SPICE netlist for transmission gate using proper Netlist class""" - # Get the SPICE netlists directly from components - nmos_spice = nfet_comp.info.get('netlist', '') - pmos_spice = pfet_comp.info.get('netlist', '') + netlist = Netlist(circuit_name='Transmission_Gate', nodes=['VIN', 'VSS', 'VOUT', 'VCC', 'VGP', 'VGN']) - if not nmos_spice or not pmos_spice: - raise ValueError("Component netlists not found") + # Get netlist objects from FET components + nfet_netlist = get_component_netlist(nfet) + pfet_netlist = get_component_netlist(pfet) - # Create the transmission gate SPICE netlist by combining the primitives - tg_spice = f"""{nmos_spice} + # Connect NMOS: D->VOUT, G->VGN, S->VIN, B->VSS + netlist.connect_netlist(nfet_netlist, [('D', 'VOUT'), ('G', 'VGN'), ('S', 'VIN'), ('B', 'VSS')]) + # Connect PMOS: D->VOUT, G->VGP, S->VIN, B->VCC + netlist.connect_netlist(pfet_netlist, [('D', 'VOUT'), ('G', 'VGP'), ('S', 'VIN'), ('B', 'VCC')]) -{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 + return netlist @cell def transmission_gate( @@ -163,12 +154,12 @@ def transmission_gate( 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 - + # Generate netlist using proper Netlist class + netlist_obj = tg_netlist(nfet, pfet) + + # Store netlist - LVS method handles both string and Netlist objects + component.info['netlist'] = netlist_obj return component diff --git a/src/glayout/pdk/mappedpdk.py b/src/glayout/pdk/mappedpdk.py index d6193ff3..35a03564 100644 --- a/src/glayout/pdk/mappedpdk.py +++ b/src/glayout/pdk/mappedpdk.py @@ -259,8 +259,6 @@ class MappedPDK(Pdk): "via4", "met5", "capmet", - "lvs_bjt", - "drc_bjt", # _pin layers "met5_pin", "met4_pin", @@ -294,8 +292,6 @@ class MappedPDK(Pdk): 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") def models_check(cls, models_obj: dict[StrictStr, StrictStr]): for model in models_obj.keys(): @@ -764,7 +760,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: