diff --git a/.github/workflows/test_python_api.yml b/.github/workflows/test_python_api.yml index bec0bf60f..118742bfa 100644 --- a/.github/workflows/test_python_api.yml +++ b/.github/workflows/test_python_api.yml @@ -20,7 +20,7 @@ jobs: strategy: max-parallel: 12 matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.10", "3.11", "3.12"] os: [ubuntu-latest] steps: diff --git a/docker/conda/Dockerfile b/docker/conda/Dockerfile index 604ed4910..e381168e4 100755 --- a/docker/conda/Dockerfile +++ b/docker/conda/Dockerfile @@ -14,6 +14,7 @@ RUN conda update conda --all -y RUN conda install python=3.10 \ yosys \ open_pdks.sky130a \ + open_pdks.gf180mcuC \ magic \ netgen \ openroad \ diff --git a/openfasoc/generators/gdsfactory-gen/glayout/interpreter.py b/openfasoc/generators/gdsfactory-gen/glayout/interpreter.py deleted file mode 100644 index 8a3aa8905..000000000 --- a/openfasoc/generators/gdsfactory-gen/glayout/interpreter.py +++ /dev/null @@ -1,234 +0,0 @@ -import argparse -import inspect -import fileinput -import io -import sys -from pathlib import Path -from typing import Optional, Callable, Literal -from gdsfactory import Component -# import primitives -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.primitives.guardring import tapring -from glayout.primitives.mimcap import mimcap, mimcap_array -from glayout.primitives.via_gen import via_array, via_stack -# import placement macros -from glayout.placement.two_transistor_interdigitized import two_transistor_interdigitized, two_nfet_interdigitized -from glayout.placement.two_transistor_place import two_transistor_place -# import routing macros -from glayout.routing.c_route import c_route -from glayout.routing.L_route import L_route -from glayout.routing.straight_route import straight_route - - -PLACE_HELP=""" -In place mode, the keyword “place” begins a sentence. -The keyword “place” is followed by one or more generators. -The generator is one of -nmos, pmos, multiplier, mimcap, mimcap_array, via_stack, via_array, tapring, two_nfet_interdigitized - -To specify arguments to a generator, you should write the argument name and value followed by a , or and - -Following the list of “circuits”, An optional configuration can be specified. -The following are examples of valid configurations -configuration xyx yxy xyx -configuration x y -configuration xxxy -configuration yyyx -And so on… - -Here is an example sentence for place mode: -place an nfet and pfet in xyx yxy xyx configuration. -place an nfet with width=5,length=7 -place an nfet with width 5 and length 7 -""" - - -######################################## -# helper functions -######################################## - -def prompt_from_list(prompt_list, convo) -> int: - for i,prompt_item in enumerate(prompt_list): - convo.print_and_update_session(str(i)+". "+str(prompt_item)) - convo.print_and_update_session("enter the number corresponding to the desired input_and_process:",False) - choosen_index = int(convo.inputstream.readline().strip().strip("\n")) - while(choosen_index>=len(prompt_list)): - convo.print_and_update_session("choosen_index out of range please try again:",False) - choosen_index = input(convo.inputstream.readline().strip().strip("\n")) - return choosen_index - -#TODO finish implement -def get_param_val_as_str(input: str, generator_name: str, param_name: str) -> str: - input = input.strip().strip(".").lower().replace(",","and").replace("="," ") - # only keep the stuff after the generator name - if input.find(generator_name)!=-1: - input = input.split(generator_name) - if len(input)<2:# no params were specfied by the user - return str() - input = input[1].strip() - else: - raise ValueError(generator_name+" was not found in input") - # only keep the stuff after the parameter name - if input.find(param_name)!=-1: - input = input.split(param_name)[1].strip() - else: - raise ValueError(param_name+" was not found in input") - # TODO this is not versatile - return input.split("and")[0] - - -######################################## -# Session state -######################################## - -class GlayoutCode: - """Store Glayout code in a way that allows both dynamic running and quick printing""" - - class PlaceInfo: - def __init__(self, generator: Callable, pdk: str, params: dict): - self.generator = generator - self.PDK = pdk # NAME of pdk variable - self.params = params # params are stored as str:str, param_name:param_val - def tostr(self)->str: - line = generator.__name__ - line += "("+self.PDK.name+"_mpdk, " - for key,val in self.params.items(): - line += key + "=" + val + ", " - line += ")" - - def __init__(self, cell_name: str, pdk: str): - """Store basic header info for this generator""" - self.toplvl_args = list() - self.PDK = pdk # NAME of pdk variable - self.name = cell_name - self.line_infos = list() - - def add_line(mode: Literal["place","route","move"], kwargs): - if mode=="place": - self.line_infos.append(PlaceInfo(**kwargs)) - elif mode=="route": - self.line_infos.append(RouteInfo(**kwargs)) - elif mode=="move": - self.line_infos.append(MoveInfo(**kwargs)) - - def dump_code() -> str: - code = str() - for line_info in line_infos: - code += line_info.tostr() + "\n" - - -class Session: - """The session stores all relevant information for producing code from a conversation""" - - # comp_options must be callable cells. The first argument must be "pdk" - generators = [nmos,pmos,multiplier,mimcap,mimcap_array,via_stack,via_array,tapring, two_nfet_interdigitized] - # supported pdks must be of mapped pdk type - supported_pdks = {0:"gf180",1:"sky130"} - - def __init__(self, inputstream: io.IOBase, outputstream: io.IOBase): - """initialize a conversation and greet the user""" - # init PDK, and io streams - self.inputstream = inputstream - self.outputstream = outputstream - self.PDK = None - # initialize metadata - self.components = dict() - self.variables = dict() - self.conversation_prompts = list() - self.conversation_responses = list() - # greet the user and load pdk - self.print_and_update_session("Hello!") - self.__load_pdk() - self.print_and_update_session("What would you like to create today?") - self.print_and_update_session("Please provide a name for the Component you want to create") - self.print_and_update_session("remember, this will be the name of your top level component: ",False) - name = inputstream.readline() - self.print_and_update_session("now, lets go through all the steps to create " + name) - # init the rest of the data - self.code = GlayoutCode(name,self.PDK.name) - self.toplevel_comp = Component(name=name) - - def __load_pdk(self): - # prompt for supportpdk - self.print_and_update_session("please specify a PDK to get started. The supported PDKs include:)") - pdk_index = prompt_from_list(Session.supported_pdks.values(),self) - pdk_name = Session.supported_pdks.get(pdk_index) - if pdk_name == "gf180": - from glayout.pdk.gf180_mapped import gf180_mapped_pdk - self.PDK = gf180_mapped_pdk - elif pdk_name == "sky130": - from glayout.pdk.sky130_mapped import sky130_mapped_pdk - self.PDK = sky130_mapped_pdk - else: - raise ValueError("specify a support pdk") - self.PDK.activate() - - def print_and_update_session(self, toprint: str, save: bool=True): - """Correctly updates the conversation prompts - then writes toprint to the output stream provided""" - if save: - self.conversation_prompts.append(str(toprint)) - self.outputstream.write(toprint+"\n") - - def process_next_input(self) -> str: - """main driver for doing things""" - response = self.inputstream.readline().strip().lower() - # parse user input - if response[0]=="h":# help - self.__help(response) - elif response[0]=="p":# place - if "configuration" in response: - raise NotImplementedError("configs not yet implemented") - self.__place(response) - elif response[0]=="r":# route - self.__route(response) - elif response[0]=="g":# dump code - self.__generate_code(response) - elif response[0]=="s":# show a component - self.__show_component(response) - else: - self.print_and_update_session("invalid input",save=False) - self.print_and_update_session("sentences must begin with either place, route, generate, or move",save=False) - # save response - self.conversation_responses["responses"].append(str(response)) - - # TODO finish implement - def __place(self, response: str): - generators = [(i,fnc.__name__) for i,fnc in enumerate(Session.generators)] - generators = [generator for generator in generators if generator[1] in response] - generator_fncs = [Session.generators[i] for i,fncname in generators] - # loop over all generators. Each generator represents a task that must be completed - func_calls = list() - for generator in generator_fncs: - - for param in inspect.signature(generator).parameters.values(): - if param.name.lower() in ["pdk"]: - continue - param_val = get_param_val_as_str(response, generator.__name__, param.name) - if (param_val=="" and param.default==inspect.Parameter.empty): - self.print_and_update_session("you did not provide a value for an argument that does not have a default",save=False) - elif param_val == "": # value not provided but param has a default - continue - else:# value was provided - pass - - - - -if __name__=="__main__": - # parse args - parser = argparse.ArgumentParser(description="Load conversation from a file") - parser.add_argument("--load_conversation", type=Path, help="Specify the file path to load a previous conversation") - args = parser.parse_args() - - # start convo and load PDK - convo = Session(inputstream=sys.stdin,outputstream=sys.stdout) - - # enter design loop - session_ongoing = True - loop_count = 0 - while(session_ongoing): - convo.print_and_update_session("task "+str(loop_count)+":") - loop_count = loop_count + 1 - convo.print_and_update_session("What do you want to do?") - diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/gf180_mapped.py b/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/gf180_mapped.py deleted file mode 100644 index 406fb619a..000000000 --- a/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/gf180_mapped.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -usage: from gf180_mapped import gf180_mapped_pdk -""" - -from gf180.layers import LAYER # , LAYER_VIEWS -from ..gf180_mapped.grules import grulesobj -from ..mappedpdk import MappedPDK -from pathlib import Path - -LAYER = LAYER.dict() -#LAYER["fusetop"]=(75, 0) -LAYER["CAP_MK"] = (117,5) - -gf180_glayer_mapping = { - "met5": "metal5", - "via4": "via4", - "met4": "metal4", - "via3": "via3", - "met3": "metal3", - "via2": "via2", - "met2": "metal2", - "via1": "via1", - "met1": "metal1", - "mcon": "contact", - "poly": "poly2", - "active_diff": "comp", - "active_tap": "comp", - "n+s/d": "nplus", - "p+s/d": "pplus", - "nwell": "nwell", - "pwell": "lvpwell", - "dnwell": "dnwell", - "capmet": "CAP_MK" -} - -# note for DRC, there is mim_option 'A'. This is the one configured for use - -gf180_lydrc_file_path = Path(__file__).resolve().parent / "gf180mcu_drc.lydrc" - - -gf180_mapped_pdk = MappedPDK( - name="gf180", - glayers=gf180_glayer_mapping, - models={ - 'nfet': 'nfet_03v3', - 'pfet': 'pfet_03v3', - 'mimcap': 'mimcap_1p0fF' - }, - layers=LAYER, - klayout_lydrc_file=gf180_lydrc_file_path, - grules=grulesobj, -) - -# configure the grid size and other settings -gf180_mapped_pdk.gds_write_settings.precision = 5*10**-9 -gf180_mapped_pdk.cell_decorator_settings.cache=False diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/mappedpdk.py b/openfasoc/generators/gdsfactory-gen/glayout/pdk/mappedpdk.py deleted file mode 100644 index b09bef1d8..000000000 --- a/openfasoc/generators/gdsfactory-gen/glayout/pdk/mappedpdk.py +++ /dev/null @@ -1,329 +0,0 @@ -""" -usage: from mappedpdk import MappedPDK -""" - -from gdsfactory.pdk import Pdk -from gdsfactory.typings import Component, PathType, Layer -from pydantic import validator, StrictStr, ValidationError -from typing import ClassVar, Optional, Any, Union, Literal, Iterable, TypedDict -from pathlib import Path -from decimal import Decimal, ROUND_UP -import tempfile -import subprocess -from decimal import Decimal -from pydantic import validate_arguments -import xml.etree.ElementTree as ET - -class MappedPDK(Pdk): - """Inherits everything from the pdk class but also requires mapping to glayers - glayers are generic layers which can be returned with get_glayer(name: str) - has_required_glayers(list[str]) is used to verify all required generic layers are - present""" - - valid_glayers: ClassVar[tuple[str]] = ( - "dnwell", - "pwell", - "nwell", - "p+s/d", - "n+s/d", - "active_diff", - "active_tap", - "poly", - "mcon", - "met1", - "via1", - "met2", - "via2", - "met3", - "via3", - "met4", - "via4", - "met5", - "capmet", - ) - - models: dict = { - "nfet": "", - "pfet": "", - "mimcap": "" - } - - glayers: dict[StrictStr, Union[StrictStr, tuple[int,int]]] - # friendly way to implement a graph - grules: dict[StrictStr, dict[StrictStr, Optional[dict[StrictStr, Any]]]] - klayout_lydrc_file: Optional[Path] = None - - @validator("models") - def models_check(cls, models_obj: dict[StrictStr, StrictStr]): - for model in models_obj.keys(): - if not model in ["nfet","pfet","mimcap"]: - raise ValueError(f"specify nfet, pfet, or mimcap models only") - return models_obj - - @validator("glayers") - def glayers_check_keys(cls, glayers_obj: dict[StrictStr, Union[StrictStr, tuple[int,int]]]): - """force people to pick glayers from a finite set of string layers that you define - checks glayers to ensure valid keys,type. Glayers must be passed as dict[str,str] - if someone tries to pass a glayers dict that has a bad key, throw an error""" - for glayer, mapped_layer in glayers_obj.items(): - if (not isinstance(glayer, str)) or (not isinstance(mapped_layer, Union[str, tuple])): - raise TypeError("glayers should be passed as dict[str, Union[StrictStr, tuple[int,int]]]") - if glayer not in cls.valid_glayers: - raise ValueError( - "glayers keys must be one of generic layers listed in class variable valid_glayers" - ) - return glayers_obj - - @validator("klayout_lydrc_file") - def lydrc_file_exists(cls, lydrc_file_path): - """Check that lydrc_file_path exists if not none""" - if lydrc_file_path != None and not lydrc_file_path.is_file(): - raise ValueError(".lydrc script: the path given is not a file") - return lydrc_file_path - - @validate_arguments - def drc( - self, - layout: Component | PathType, - output_dir_or_file: Optional[PathType] = None, - ): - """Returns true if the layout is DRC clean and false if not - Also saves detailed results to output_dir_or_file location as lyrdb - layout can be passed as a file path or gdsfactory component""" - if not self.klayout_lydrc_file: - raise NotImplementedError("no drc script for this pdk") - # find layout gds file path - tempdir = None - if isinstance(layout, Component): - tempdir = tempfile.TemporaryDirectory() - layout_path = Path(layout.write_gds(gdsdir=tempdir.name)).resolve() - elif isinstance(layout, PathType): - layout_path = Path(layout).resolve() - else: - raise TypeError("layout should be a Component, Path, or string") - if not layout_path.is_file(): - raise ValueError("layout must exist, the path given is not a file") - # find report file path, if None then use current directory - report_path = ( - Path(output_dir_or_file).resolve() - if output_dir_or_file - else Path.cwd().resolve() - ) - if report_path.is_dir(): - report_path = Path( - report_path - / str( - self.name - + layout_path.name.replace(layout_path.suffix, "") - + "_drcreport.lyrdb" - ) - ) - elif not report_path.is_file(): - raise ValueError("report_path must be file or dir") - # run klayout drc - drc_args = [ - "klayout", - "-b", - "-r", - str(self.klayout_lydrc_file), - "-rd", - "input=" + str(layout_path), - "-rd", - "report=" + str(report_path), - ] - rtr_code = subprocess.Popen(drc_args).wait() - if rtr_code: - raise RuntimeError("error running klayout DRC") - # clean up and return - if tempdir: - tempdir.cleanup() - # there is a drc parsing open-source at: - # https://github.com/google/globalfoundries-pdk-libs-gf180mcu_fd_pr/blob/main/rules/klayout/drc - # eventually I can return more info on the drc run, but for now just void and view the lyrdb in klayout - - # Open DRC output XML file - drc_tree = ET.parse(report_path.resolve()) - drc_root = drc_tree.getroot() - if drc_root.tag != "report-database": - raise TypeError("DRC report file is not a valid report-database") - # Check if DRC passed - drc_error_count = len(drc_root[7]) - return (drc_error_count == 0) - - @validate_arguments - def has_required_glayers(self, layers_required: list[str]): - """Raises ValueError if any of the generic layers in layers_required: list[str] - are not mapped to anything in the pdk.glayers dictionary - also checks that the values in the glayers dictionary map to real Pdk layers""" - for layer in layers_required: - if layer not in self.glayers: - raise ValueError( - f"{layer!r} not in self.glayers {list(self.glayers.keys())}" - ) - if isinstance(self.glayers[layer], str): - self.validate_layers([self.glayers[layer]]) - elif not isinstance(self.glayers[layer], tuple): - raise TypeError("glayer mapped value should be str or tuple[int,int]") - - - @validate_arguments - def layer_to_glayer(self, layer: tuple[int, int]) -> str: - """if layer provided corresponds to a glayer, will return a glayer - else will raise an exception - takes layer as a tuple(int,int)""" - # lambda for finding last matching key in dict from val - find_last = lambda val, d: [x for x, y in d.items() if y == val].pop() - if layer in self.glayers.values(): - return find_last(layer, self.glayers) - elif self.layers is not None: - # find glayer verfying presence along the way - pdk_real_layers = self.layers.values() - if layer in pdk_real_layers: - layer_name = find_last(layer, self.layers) - if layer_name in self.glayers.values(): - glayer_name = find_last(layer_name, self.glayers) - else: - raise ValueError("layer does not correspond to a glayer") - else: - raise ValueError("layer is not a layer present in the pdk") - return glayer_name - else: - raise ValueError("layer might not be a layer present in the pdk") - - # TODO: implement LayerSpec type - @validate_arguments - def get_glayer(self, layer: str) -> Layer: - """Returns the pdk layer from the generic layer name""" - direct_mapping = self.glayers[layer] - if isinstance(direct_mapping, tuple): - return direct_mapping - else: - return self.get_layer(direct_mapping) - - @validate_arguments - def get_grule( - self, glayer1: str, glayer2: Optional[str] = None, return_decimal = False - ) -> dict[StrictStr, Union[float,Decimal]]: - """Returns a dictionary describing the relationship between two layers - If one layer is specified, returns a dictionary with all intra layer rules""" - if glayer1 not in MappedPDK.valid_glayers: - raise ValueError("get_grule, " + str(glayer1) + " not valid glayer") - # decide if two or one inputs and set rules_dict accordingly - rules_dict = None - if glayer2 is not None: - if glayer2 not in MappedPDK.valid_glayers: - raise ValueError("get_grule, " + str(glayer2) + " not valid glayer") - rules_dict = self.grules.get(glayer1, dict()).get(glayer2) - if rules_dict is None or rules_dict == {}: - rules_dict = self.grules.get(glayer2, dict()).get(glayer1) - else: - glayer2 = glayer1 - rules_dict = self.grules.get(glayer1, dict()).get(glayer1) - # error check, convert type, and return - if rules_dict is None or rules_dict == {}: - raise NotImplementedError( - "no rules found between " + str(glayer1) + " and " + str(glayer2) - ) - for rule in rules_dict: - if type(rule) == float and return_decimal: - rules_dict[rule] = Decimal(str(rule)) - return rules_dict - - @classmethod - def is_routable_glayer(cls, glayer: StrictStr): - return any(hint in glayer for hint in ["met", "active", "poly"]) - - # TODO: implement - @classmethod - def from_gf_pdk( - cls, - gfpdk: Pdk, - **kwargs - ): - """Construct a mapped pdk from an existing pdk and the extra parts of MappedPDK - grid is the grid size in nm""" - # input type and value validation - if not isinstance(gfpdk, Pdk): - raise TypeError("from_gf_pdk: gfpdk arg only accepts GDSFactory pdk type") - # create argument dictionary - passargs = dict() - # pdk args - passargs["name"]=gfpdk.name - #passargs["cross_sections"]=gfpdk.cross_sections - #passargs["cells"]=gfpdk.cells - #passargs["symbols"]=gfpdk.symbols - #passargs["default_symbol_factory"]=gfpdk.default_symbol_factory - #passargs["containers"]=gfpdk.containers - #passargs["base_pdk"]=gfpdk.base_pdk - #passargs["default_decorator"]=gfpdk.default_decorator - passargs["layers"]=gfpdk.layers - #passargs["layer_stack"]=gfpdk.layer_stack - #passargs["layer_views"]=gfpdk.layer_views#??? layer view broken??? -# passargs["layer_transitions"]=gfpdk.layer_transitions -# passargs["sparameters_path"]=gfpdk.sparameters_path -# passargs["modes_path"]=gfpdk.modes_path -# passargs["interconnect_cml_path"]=gfpdk.interconnect_cml_path -# passargs["warn_off_grid_ports"]=gfpdk.warn_off_grid_ports -# passargs["constants"]=gfpdk.constants -# passargs["materials_index"]=gfpdk.materials_index -# passargs["routing_strategies"]=gfpdk.routing_strategies -# passargs["circuit_yaml_parser"]=gfpdk.circuit_yaml_parser -# passargs["gds_write_settings"]=gfpdk.gds_write_settings -# passargs["oasis_settings"]=gfpdk.oasis_settings -# passargs["cell_decorator_settings"]=gfpdk.cell_decorator_settings -# passargs["bend_points_distance"]=gfpdk.bend_points_distance - # MappedPDK args override existing args - passargs.update(kwargs) - # create and return MappedPDK - mappedpdk = MappedPDK(**passargs) - return mappedpdk - - # util methods - @validate_arguments - def util_max_metal_seperation(self, metal_levels: Union[list[int],list[str], str, int] = range(1,6)) -> float: - """returns the maximum of the min_seperation rule for all layers specfied - although the name of this function is util_max_metal_seperation, layers do not have to be metals - you can specify non metals by using metal_levels=list of glayers - if metal_levels is list of int, integers are converted to metal levels - if a single int is provided, all metals below and including that int level are considerd - by default this function returns the maximum metal seperation of metals1-5 - """ - if type(metal_levels)==int: - metal_levels = range(1,metal_levels+1) - metal_levels = metal_levels if isinstance(metal_levels,Iterable) else [metal_levels] - if len(metal_levels)<1: - raise ValueError("metal levels cannot be empty list") - if type(metal_levels[0])==int: - metal_levels = [f"met{i}" for i in metal_levels] - sep_rules = list() - for met in metal_levels: - sep_rules.append(self.get_grule(met)["min_separation"]) - return self.snap_to_2xgrid(max(sep_rules)) - - @validate_arguments - def snap_to_2xgrid(self, dims: Union[list[Union[float,Decimal]], Union[float,Decimal]], return_type: Literal["decimal","float","same"]="float", snap4: bool=False) -> Union[list[Union[float,Decimal]], Union[float,Decimal]]: - """snap all numbers in dims to double the grid size. - This is useful when a generator accepts a size or dimension argument - because there is a chance the cell may be centered (resulting in off grid components) - args: - dims = a list OR single number specifying the dimensions to snap to grid - return_type = return a decimal, float, or the same type that was passed to the function - snap4: snap to 4xgrid (Defualt false) - """ - dims = dims if isinstance(dims, Iterable) else [dims] - dimtype_in = type(dims[0]) - dims = [Decimal(str(dim)) for dim in dims] # process in decimals - grid = 2 * Decimal(str(self.grid_size)) - grid = grid if grid else Decimal('0.001') - grid = 2*grid if snap4 else grid - # snap dims to grid - snapped_dims = list() - for dim in dims: - snapped_dim = grid * (dim / grid).quantize(1, rounding=ROUND_UP) - snapped_dims.append(snapped_dim) - # convert to correct type - if return_type=="float" or (return_type=="same" and dimtype_in==float): - snapped_dims = [float(snapped_dim) for snapped_dim in snapped_dims] - # correctly return list or single element - return snapped_dims[0] if len(snapped_dims)==1 else snapped_dims - diff --git a/openfasoc/generators/gdsfactory-gen/glayout/requirements.txt b/openfasoc/generators/gdsfactory-gen/glayout/requirements.txt deleted file mode 100644 index 62cd6a359..000000000 --- a/openfasoc/generators/gdsfactory-gen/glayout/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -gf180 -prettyprinttree diff --git a/openfasoc/generators/gdsfactory-gen/glayout/README.md b/openfasoc/generators/glayout/README.md similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/README.md rename to openfasoc/generators/glayout/README.md diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/PortTreeExample.png b/openfasoc/generators/glayout/docs/PortTreeExample.png similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/PortTreeExample.png rename to openfasoc/generators/glayout/docs/PortTreeExample.png diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/straight_route_def_beh.png b/openfasoc/generators/glayout/docs/straight_route_def_beh.png similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/straight_route_def_beh.png rename to openfasoc/generators/glayout/docs/straight_route_def_beh.png diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/L_route_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/L_route_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/L_route_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/L_route_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/c_route_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/c_route_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/c_route_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/c_route_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/diff_pair_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/diff_pair_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/diff_pair_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/diff_pair_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/mimcap_array_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/mimcap_array_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/mimcap_array_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/mimcap_array_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/mimcap_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/mimcap_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/mimcap_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/mimcap_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/multiplier_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/multiplier_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/multiplier_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/multiplier_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/nmos_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/nmos_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/nmos_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/nmos_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/opamp_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/opamp_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/opamp_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/opamp_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/pmos_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/pmos_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/pmos_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/pmos_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/straight_route_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/straight_route_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/straight_route_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/straight_route_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/tapring_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/tapring_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/tapring_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/tapring_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/via_stack_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/via_stack_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/via_stack_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/via_stack_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/viaarray_v1_tree.txt b/openfasoc/generators/glayout/docs/txt_port_trees/viaarray_v1_tree.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/docs/txt_port_trees/viaarray_v1_tree.txt rename to openfasoc/generators/glayout/docs/txt_port_trees/viaarray_v1_tree.txt diff --git a/openfasoc/generators/gdsfactory-gen/glayout/__init__.py b/openfasoc/generators/glayout/glayout/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/__init__.py rename to openfasoc/generators/glayout/glayout/__init__.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/__init__.py b/openfasoc/generators/glayout/glayout/flow/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/components/__init__.py rename to openfasoc/generators/glayout/glayout/flow/__init__.py diff --git a/openfasoc/generators/glayout/glayout/flow/components/CrossCoupledInverters.convo b/openfasoc/generators/glayout/glayout/flow/components/CrossCoupledInverters.convo new file mode 100644 index 000000000..8603e7f70 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/components/CrossCoupledInverters.convo @@ -0,0 +1,18 @@ +CrossCoupledInverters +#Create a float parameter called ccinvs_nfet_width +#Create a float parameter called ccinvs_pfet_width +#Create a float parameter called ccinvs_nfet_length +#Create a float parameter called ccinvs_pfet_length +Create an int parameter called ccinvs_numfingers +place 4 interdigitated transistors called ccinvs with numcols=ccinvs_numfingers, top_row_device="pfet", bottom_row_device="nfet" +# sources are connected to pwr and gnd respectively +route between ccinvs_top_A_source_E and ccinvs_top_B_source_E +route between ccinvs_bottom_A_source_E and ccinvs_bottom_B_source_E +# output of each inverter goes to input of the other inverter +route between ccinvs_top_A_drain_E and ccinvs_top_B_gate_E +route between ccinvs_bottom_A_drain_E and ccinvs_bottom_B_gate_E +route between ccinvs_top_B_drain_E and ccinvs_top_A_gate_E +route between ccinvs_bottom_B_drain_E and ccinvs_bottom_A_gate_E +# connect nfet and pfet of each inverter at the gate +route between ccinvs_top_B_gate_E and ccinvs_bottom_B_gate_E +route between ccinvs_top_A_gate_W and ccinvs_bottom_A_gate_W \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/flow/components/CrossCoupledInverters_cell.py b/openfasoc/generators/glayout/glayout/flow/components/CrossCoupledInverters_cell.py new file mode 100644 index 000000000..387c3a857 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/components/CrossCoupledInverters_cell.py @@ -0,0 +1,51 @@ +#### +# Compiled Glayout +# Apache License +# Version 2.0, January 2004 +# http://www.apache.org/licenses/ +# 2024-05-22 17:21:55.826288 + +from glayout.flow.pdk.mappedpdk import MappedPDK +from gdsfactory import Component +from glayout.flow.pdk.util.comp_utils import move, movex, movey, prec_ref_center, evaluate_bbox, center_to_edge_distance +from glayout.flow.pdk.util.port_utils import remove_ports_with_prefix +from glayout.flow.primitives.fet import nmos +from glayout.flow.primitives.fet import pmos +from glayout.flow.primitives.guardring import tapring +from glayout.flow.primitives.mimcap import mimcap +from glayout.flow.primitives.mimcap import mimcap_array +from glayout.flow.primitives.via_gen import via_stack +from glayout.flow.primitives.via_gen import via_array +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.placement.four_transistor_interdigitized import generic_4T_interdigitzed +from glayout.flow.placement.two_transistor_interdigitized import two_pfet_interdigitized +from glayout.flow.components.diff_pair import diff_pair_generic +from glayout.flow.routing.smart_route import smart_route +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 + +def CrossCoupledInverters_cell( + pdk: MappedPDK, + ccinvs_numfingers: int, +): + pdk.activate() + CrossCoupledInverters = Component(name="CrossCoupledInverters") + maxmetalsep = pdk.util_max_metal_seperation() + double_maxmetalsep = 2*pdk.util_max_metal_seperation() + triple_maxmetalsep = 3*pdk.util_max_metal_seperation() + quadruple_maxmetalsep = 4*pdk.util_max_metal_seperation() + # placing ccinvs centered at the origin + ccinvs = generic_4T_interdigitzed(pdk,**{'numcols': ccinvs_numfingers, 'top_row_device': "pfet", 'bottom_row_device': "nfet"}) + ccinvs_ref = prec_ref_center(ccinvs) + CrossCoupledInverters.add(ccinvs_ref) + CrossCoupledInverters.add_ports(ccinvs_ref.get_ports_list(),prefix="ccinvs_") + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_top_A_source_E"],CrossCoupledInverters.ports["ccinvs_top_B_source_E"],ccinvs_ref,CrossCoupledInverters,**{}) + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_bottom_A_source_E"],CrossCoupledInverters.ports["ccinvs_bottom_B_source_E"],ccinvs_ref,CrossCoupledInverters,**{}) + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_top_A_drain_E"],CrossCoupledInverters.ports["ccinvs_top_B_gate_E"],ccinvs_ref,CrossCoupledInverters,**{}) + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_bottom_A_drain_E"],CrossCoupledInverters.ports["ccinvs_bottom_B_gate_E"],ccinvs_ref,CrossCoupledInverters,**{}) + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_top_B_drain_E"],CrossCoupledInverters.ports["ccinvs_top_A_gate_E"],ccinvs_ref,CrossCoupledInverters,**{}) + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_bottom_B_drain_E"],CrossCoupledInverters.ports["ccinvs_bottom_A_gate_E"],ccinvs_ref,CrossCoupledInverters,**{}) + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_top_B_gate_E"],CrossCoupledInverters.ports["ccinvs_bottom_B_gate_E"],ccinvs_ref,CrossCoupledInverters,**{}) + CrossCoupledInverters << smart_route(pdk,CrossCoupledInverters.ports["ccinvs_top_A_gate_W"],CrossCoupledInverters.ports["ccinvs_bottom_A_gate_W"],ccinvs_ref,CrossCoupledInverters,**{}) + return CrossCoupledInverters diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/__init__.py b/openfasoc/generators/glayout/glayout/flow/components/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/__init__.py rename to openfasoc/generators/glayout/glayout/flow/components/__init__.py diff --git a/openfasoc/generators/glayout/glayout/flow/components/current_mirror.py b/openfasoc/generators/glayout/glayout/flow/components/current_mirror.py new file mode 100644 index 000000000..dc41ef060 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/components/current_mirror.py @@ -0,0 +1,107 @@ +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.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 +from gdsfactory.component import Component +from typing import Optional + +#@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 = top_level << 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 = top_level << 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 + top_level << L_route(pdk, interdigitized_fets.ports['A_drain_W'], gate_short.ports['con_N'], viaoffset=False, fullbottom=False) + + # add the tie layer + if with_tie: + tap_sep = max( + pdk.util_max_metal_seperation(), + pdk.get_grule("active_diff", "active_tap")["min_separation"], + ) + tap_sep += pdk.get_grule("p+s/d", "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 = "p+s/d", 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["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 + 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_") + + # 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 << interdigitized_fets + return top_level \ No newline at end of file diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair.py b/openfasoc/generators/glayout/glayout/flow/components/diff_pair.py similarity index 87% rename from openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair.py rename to openfasoc/generators/glayout/glayout/flow/components/diff_pair.py index d7c402092..a2c74e153 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair.py +++ b/openfasoc/generators/glayout/glayout/flow/components/diff_pair.py @@ -1,19 +1,30 @@ +from typing import Optional, Union + from gdsfactory.cell import cell from gdsfactory.component import Component, copy from gdsfactory.components.rectangle import rectangle -from glayout.primitives.fet import nmos, pmos -from glayout.pdk.mappedpdk import MappedPDK -from typing import Optional, Union from gdsfactory.routing.route_quad import route_quad from gdsfactory.routing.route_sharp import route_sharp -from glayout.routing.c_route import c_route -from glayout.routing.straight_route import straight_route -from glayout.pdk.util.comp_utils import movex, movey, evaluate_bbox, align_comp_to_port -from glayout.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports, get_orientation, set_port_orientation -from glayout.primitives.via_gen import via_stack -from glayout.primitives.guardring import tapring -from glayout.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.spice import Netlist +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 + def diff_pair_netlist(fetL: Component, fetR: Component) -> Netlist: diff_pair_netlist = Netlist(circuit_name='DIFF_PAIR', nodes=['VP', 'VN', 'VDD1', 'VDD2', 'VTAIL', 'B']) @@ -25,7 +36,6 @@ def diff_pair_netlist(fetL: Component, fetR: Component) -> Netlist: fetR.info['netlist'], [('D', 'VDD2'), ('G', 'VN'), ('S', 'VTAIL'), ('B', 'B')] ) - return diff_pair_netlist @cell @@ -133,7 +143,7 @@ def diff_pair( bar_comp = rectangle(centered=True,size=(abs(b_topr.xmax-a_topl.xmin), b_topr.ports["multiplier_0_gate_E"].width),layer=pdk.get_glayer("met2")) bar_plus = (diffpair << bar_comp).movey(diffpair.ymax + bar_comp.ymax + pdk.get_grule("met2")["min_separation"]) PLUSgate_routeW = diffpair << c_route(pdk, a_topl.ports["multiplier_0_gate_W"], bar_plus.ports["e1"], extension=get_left_extension(bar_plus)) - #lay bar minus and MINUSgate_routeE + # lay bar minus and MINUSgate_routeE plus_minus_seperation = max(pdk.get_grule("met2")["min_separation"], plus_minus_seperation) bar_minus = (diffpair << bar_comp).movey(diffpair.ymax +bar_comp.ymax + plus_minus_seperation) MINUSgate_routeE = diffpair << c_route(pdk, b_topr.ports["multiplier_0_gate_E"], bar_minus.ports["e3"], extension=get_right_extension(bar_minus)) @@ -162,4 +172,18 @@ def diff_pair( - +@cell +def diff_pair_generic( + 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: + 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 diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair_cmirrorbias.py b/openfasoc/generators/glayout/glayout/flow/components/diff_pair_cmirrorbias.py similarity index 87% rename from openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair_cmirrorbias.py rename to openfasoc/generators/glayout/glayout/flow/components/diff_pair_cmirrorbias.py index 6a975d4da..4162512f6 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair_cmirrorbias.py +++ b/openfasoc/generators/glayout/glayout/flow/components/diff_pair_cmirrorbias.py @@ -2,17 +2,17 @@ from gdsfactory.component import Component, copy from gdsfactory.component_reference import ComponentReference from gdsfactory.components.rectangle import rectangle -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.components.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 glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.components.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 import c_route +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.pdk.util.comp_utils import ( +from glayout.flow.pdk.util.comp_utils import ( evaluate_bbox, prec_ref_center, movex, @@ -23,7 +23,7 @@ align_comp_to_port, get_padding_points_cc, ) -from glayout.pdk.util.port_utils import ( +from glayout.flow.pdk.util.port_utils import ( rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, @@ -31,12 +31,12 @@ set_port_orientation, rename_component_ports, ) -from glayout.routing.straight_route import straight_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid from pydantic import validate_arguments -from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.spice import Netlist -from glayout.components.stacked_current_mirror import current_mirror_netlist +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.spice import Netlist +from glayout.flow.components.stacked_current_mirror import current_mirror_netlist def diff_pair_ibias_netlist(center_diffpair: Component, current_mirror: Component, antenna_diode: Component) -> Netlist: netlist = Netlist( diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair_stackedcmirror.py b/openfasoc/generators/glayout/glayout/flow/components/diff_pair_stackedcmirror.py similarity index 86% rename from openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair_stackedcmirror.py rename to openfasoc/generators/glayout/glayout/flow/components/diff_pair_stackedcmirror.py index 7e98e6118..61e78a476 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/diff_pair_stackedcmirror.py +++ b/openfasoc/generators/glayout/glayout/flow/components/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.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.components.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 glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.components.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 import c_route +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.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.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.routing.straight_route import straight_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +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 pydantic import validate_arguments -from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.components.diff_pair_cmirrorbias import diff_pair_ibias -from glayout.components.stacked_current_mirror import stacked_nfet_current_mirror -from glayout.components.differential_to_single_ended_converter import differential_to_single_ended_converter -from glayout.components.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter +from glayout.flow.components.diff_pair_cmirrorbias import diff_pair_ibias +from glayout.flow.components.stacked_current_mirror import stacked_nfet_current_mirror +from glayout.flow.components.differential_to_single_ended_converter import differential_to_single_ended_converter +from glayout.flow.components.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter @validate_arguments diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/differential_to_single_ended_converter.py b/openfasoc/generators/glayout/glayout/flow/components/differential_to_single_ended_converter.py similarity index 91% rename from openfasoc/generators/gdsfactory-gen/glayout/components/differential_to_single_ended_converter.py rename to openfasoc/generators/glayout/glayout/flow/components/differential_to_single_ended_converter.py index 6c7c46f9c..9fccfcb47 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/differential_to_single_ended_converter.py +++ b/openfasoc/generators/glayout/glayout/flow/components/differential_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.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.components.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 glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.components.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 import c_route +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.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.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.routing.straight_route import straight_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +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 pydantic import validate_arguments -from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.spice import Netlist +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.spice import Netlist diff --git a/openfasoc/generators/glayout/glayout/flow/components/opamp.convo b/openfasoc/generators/glayout/glayout/flow/components/opamp.convo new file mode 100644 index 000000000..b54868553 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/components/opamp.convo @@ -0,0 +1,67 @@ +opamp +import diff_pair from glayout.flow.components.diff_pair +import generic_4T_interdigitzed from glayout.flow.placement.four_transistor_interdigitized +import current_mirror from glayout.flow.components.current_mirror +create an int parameter called numcols +place generic_4T_interdigitzed called mydiffload with numcols numcols top_kwargs {'tie_layers':("met3","met2")}top_row_device 'pfet' bottom_row_device 'pfet' +place a diff_pair called mydiff with width 5 fingers numcols dummy True substrate_tap True +move mydiffload to north of mydiff +route between mydiffload_top_A_source_W and mydiffload_top_B_source_W +route between mydiffload_top_A_gate_W and mydiffload_top_B_gate_W using c_route with extension=2 +route between mydiffload_bottom_A_gate_W and mydiffload_bottom_B_gate_W using c_route with extension=2 +route between mydiffload_bottom_A_gate_E and mydiffload_top_A_gate_E +route between mydiffload_bottom_A_gate_E and mydiffload_top_A_gate_E +route between mydiffload_top_A_drain_W and mydiffload_bottom_A_source_W +route between mydiffload_top_B_drain_E and mydiffload_bottom_B_source_E using c_route with extension=2 +route between mydiffload_bottom_B_drain_W and mydiffload_bottom_B_gate_W +route between mydiffload_top_A_drain_W and mydiff_bl_multiplier_0_drain_W using c_route with extension=4 +route between mydiffload_top_B_drain_E and mydiff_br_multiplier_0_drain_E using c_route with extension=4 +place a current_mirror called mycurrmirror with numcols numcols device 'nfet' with_dummy False +move mycurrmirror to south of mydiff +route between mydiff_bl_multiplier_0_source_W and mycurrmirror_fet_B_0_source_W using c_route with extension=3 +create a float parameter called load_pfet_width +create an int parameter called load_pfet_fingers +create an int parameter called load_pfet_multipliers +place a pfet called load_pfet_left with width load_pfet_width fingers load_pfet_fingers multipliers load_pfet_multipliers +# move load_pfet_left by -20,20 +move load_pfet_left to left of mydiff +move load_pfet_left to north of mydiff +move load_pfet_left to left of mydiffload +place a pfet called load_pfet_right with width load_pfet_width fingers load_pfet_fingers multipliers load_pfet_multipliers +# move load_pfet_right by 20,20 +move load_pfet_right to right of mydiff +move load_pfet_right to north of mydiff +move load_pfet_right to right of mydiffload +route between load_pfet_right_multiplier_0_gate_con_N and load_pfet_left_multiplier_0_gate_con_N using c_route with extension=18 width1=0.8 width2=0.8 +route between load_pfet_right_multiplier_0_drain_con_N and load_pfet_left_multiplier_0_drain_con_N using c_route with extension=8 width1=0.8 width2=0.8 +route between load_pfet_right_multiplier_0_source_con_N and load_pfet_left_multiplier_0_source_con_N using c_route with extension=5.5 viaoffset=(True, False) fullbottom=True width1=0.8 width2=0.8 +create an int parameter called load_curr_source_fingers +create a float parameter called load_curr_source_width_ref +create a float parameter called load_curr_source_width_mirr +place an nfet called load_curr_source_ref with width load_curr_source_width_ref fingers load_curr_source_fingers multipliers 2 +move load_curr_source to left of mydiff +move load_curr_source_ref to the south mydiff +# move load_curr_source_ref by -20,0 +move load_curr_source_ref to left of mycurrmirror +place an nfet called load_curr_source_mirr with width load_curr_source_width_mirr fingers load_curr_source_fingers multipliers 2 +move load_curr_source_mirr to right of mydiff +move load_curr_source_mirr to the south mydiff +move load_curr_source_mirr to right of mycurrmirror +# move load_curr_source_mirr by 20,0 +route between load_curr_source_ref_multiplier_0_gate_con_S and load_curr_source_mirr_multiplier_0_gate_con_S using c_route with extension=3 width1=0.8 width2=0.8 +route between load_curr_source_ref_multiplier_0_drain_con_S and load_curr_source_mirr_multiplier_0_drain_con_S using c_route with extension=12 viaoffset=(False, True) width1=0.8 width2=0.8 +route between load_curr_source_ref_multiplier_0_source_con_S and load_curr_source_mirr_multiplier_0_source_con_S using c_route with extension=14 width1=0.8 width2=0.8 +route between load_curr_source_ref_multiplier_0_gate_E and load_curr_source_ref_multiplier_0_drain_E using c_route with extension=3 +# connect stage1 to stage2 pmos gate +route between mydiff_tr_multiplier_0_drain_W and load_pfet_right_multiplier_0_gate_con_S using L_route +route between load_pfet_right_multiplier_0_drain_con_S and load_curr_source_mirr_multiplier_0_drain_W using L_route +create a tuple parameter called mim_cap_size +create an int parameter called mim_cap_rows +place a mimcap array called load_miller_cap with rows mim_cap_rows columns 2 size mim_cap_size +# move load_miller_cap by 45,20 +move load_miller_cap to right of mydiff +move load_miller_cap to north of mydiff +move load_miller_cap to right of load_pfet_right +route between mydiffload_top_B_0_drain_N and load_miller_cap_row0_col0_top_met_N using c_route with extension=15 +# route between load_pfet_right_multiplier_0_drain_E and load_miller_cap_row0_col0_top_met_W using straight_route +route between load_pfet_right_multiplier_0_drain_con_S and load_miller_cap_row0_col0_top_met_S using c_route with width1=1.2 width2=1.2 diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/opamp.py b/openfasoc/generators/glayout/glayout/flow/components/opamp.py similarity index 88% rename from openfasoc/generators/gdsfactory-gen/glayout/components/opamp.py rename to openfasoc/generators/glayout/glayout/flow/components/opamp.py index c3dc765c4..e63a8d0ca 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/opamp.py +++ b/openfasoc/generators/glayout/glayout/flow/components/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.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.components.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 glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.components.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 import c_route +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.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.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.routing.straight_route import straight_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +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 pydantic import validate_arguments -from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.spice import Netlist +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.spice import Netlist -from glayout.components.opamp_twostage import opamp_twostage -from glayout.components.stacked_current_mirror import current_mirror_netlist +from glayout.flow.components.opamp_twostage import opamp_twostage +from glayout.flow.components.stacked_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/openfasoc/generators/glayout/glayout/flow/components/opamp_cell.py b/openfasoc/generators/glayout/glayout/flow/components/opamp_cell.py new file mode 100644 index 000000000..ab8627368 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/components/opamp_cell.py @@ -0,0 +1,196 @@ +#### +# Compiled Glayout +# Apache License +# Version 2.0, January 2004 +# http://www.apache.org/licenses/ +# 2024-05-16 19:29:27.736502 + +from glayout.flow.pdk.mappedpdk import MappedPDK +from gdsfactory import Component +from glayout.flow.pdk.util.comp_utils import move, movex, movey, prec_ref_center, evaluate_bbox, center_to_edge_distance +from glayout.flow.pdk.util.port_utils import remove_ports_with_prefix +from glayout.flow.primitives.fet import nmos +from glayout.flow.primitives.fet import pmos +from glayout.flow.primitives.guardring import tapring +from glayout.flow.primitives.mimcap import mimcap +from glayout.flow.primitives.mimcap import mimcap_array +from glayout.flow.primitives.via_gen import via_stack +from glayout.flow.primitives.via_gen import via_array +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.placement.four_transistor_interdigitized import generic_4T_interdigitzed +from glayout.flow.placement.two_transistor_interdigitized import two_pfet_interdigitized +from glayout.flow.components.diff_pair import diff_pair_generic +from glayout.flow.routing.smart_route import smart_route +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.components.diff_pair import diff_pair +from glayout.flow.placement.four_transistor_interdigitized import generic_4T_interdigitzed +from glayout.flow.components.current_mirror import current_mirror + +def opamp_cell( + pdk: MappedPDK, + numcols: int, + load_pfet_width: float, + load_pfet_fingers: int, + load_pfet_multipliers: int, + load_curr_source_fingers: int, + load_curr_source_width_ref: float, + load_curr_source_width_mirr: float, + mim_cap_size: tuple, + mim_cap_rows: int, +): + pdk.activate() + opamp = Component(name="opamp") + maxmetalsep = pdk.util_max_metal_seperation() + double_maxmetalsep = 2*pdk.util_max_metal_seperation() + triple_maxmetalsep = 3*pdk.util_max_metal_seperation() + quadruple_maxmetalsep = 4*pdk.util_max_metal_seperation() + # placing mydiffload centered at the origin + mydiffload = generic_4T_interdigitzed(pdk,**{'numcols': numcols, 'top_kwargs': { "tie_layers" : ( "met3" , "met2" ) }, 'top_row_device': "pfet", 'bottom_row_device': "pfet"}) + mydiffload_ref = prec_ref_center(mydiffload) + opamp.add(mydiffload_ref) + opamp.add_ports(mydiffload_ref.get_ports_list(),prefix="mydiffload_") + # placing mydiff centered at the origin + mydiff = diff_pair(pdk,**{'width': 5, 'fingers': numcols, 'dummy': True, 'substrate_tap': True}) + mydiff_ref = prec_ref_center(mydiff) + opamp.add(mydiff_ref) + opamp.add_ports(mydiff_ref.get_ports_list(),prefix="mydiff_") + # move mydiffload north mydiff + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiff_ref,2) + center_to_edge_distance(mydiffload_ref,4)) + movey(mydiffload_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[1])) + remove_ports_with_prefix(opamp,"mydiffload_") + opamp.add_ports(mydiffload_ref.get_ports_list(),prefix="mydiffload_") + opamp << smart_route(pdk,opamp.ports["mydiffload_top_A_source_W"],opamp.ports["mydiffload_top_B_source_W"],mydiffload_ref,opamp,**{}) + opamp << c_route(pdk,opamp.ports["mydiffload_top_A_gate_W"],opamp.ports["mydiffload_top_B_gate_W"],**{'extension': 2}) + opamp << c_route(pdk,opamp.ports["mydiffload_bottom_A_gate_W"],opamp.ports["mydiffload_bottom_B_gate_W"],**{'extension': 2}) + opamp << smart_route(pdk,opamp.ports["mydiffload_bottom_A_gate_E"],opamp.ports["mydiffload_top_A_gate_E"],mydiffload_ref,opamp,**{}) + opamp << smart_route(pdk,opamp.ports["mydiffload_bottom_A_gate_E"],opamp.ports["mydiffload_top_A_gate_E"],mydiffload_ref,opamp,**{}) + opamp << smart_route(pdk,opamp.ports["mydiffload_top_A_drain_W"],opamp.ports["mydiffload_bottom_A_source_W"],mydiffload_ref,opamp,**{}) + opamp << c_route(pdk,opamp.ports["mydiffload_top_B_drain_E"],opamp.ports["mydiffload_bottom_B_source_E"],**{'extension': 2}) + opamp << smart_route(pdk,opamp.ports["mydiffload_bottom_B_drain_W"],opamp.ports["mydiffload_bottom_B_gate_W"],mydiffload_ref,opamp,**{}) + opamp << c_route(pdk,opamp.ports["mydiffload_top_A_drain_W"],opamp.ports["mydiff_bl_multiplier_0_drain_W"],**{'extension': 4}) + opamp << c_route(pdk,opamp.ports["mydiffload_top_B_drain_E"],opamp.ports["mydiff_br_multiplier_0_drain_E"],**{'extension': 4}) + # placing mycurrmirror centered at the origin + mycurrmirror = current_mirror(pdk,**{'numcols': numcols, 'device': "nfet", 'with_dummy': False}) + mycurrmirror_ref = prec_ref_center(mycurrmirror) + opamp.add(mycurrmirror_ref) + opamp.add_ports(mycurrmirror_ref.get_ports_list(),prefix="mycurrmirror_") + # move mycurrmirror south mydiff + relativemovcorrection_0 = -1*(maxmetalsep + center_to_edge_distance(mydiff_ref,4) + center_to_edge_distance(mycurrmirror_ref,2)) + movey(mycurrmirror_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[1])) + remove_ports_with_prefix(opamp,"mycurrmirror_") + opamp.add_ports(mycurrmirror_ref.get_ports_list(),prefix="mycurrmirror_") + opamp << c_route(pdk,opamp.ports["mydiff_bl_multiplier_0_source_W"],opamp.ports["mycurrmirror_fet_B_0_source_W"],**{'extension': 3}) + # placing load_pfet_left centered at the origin + load_pfet_left = pmos(pdk,**{'width': load_pfet_width, 'fingers': load_pfet_fingers, 'multipliers': load_pfet_multipliers}) + load_pfet_left_ref = prec_ref_center(load_pfet_left) + opamp.add(load_pfet_left_ref) + opamp.add_ports(load_pfet_left_ref.get_ports_list(),prefix="load_pfet_left_") + # move load_pfet_left left mydiff + relativemovcorrection_0 = -1*(maxmetalsep + center_to_edge_distance(mydiff_ref,1) + center_to_edge_distance(load_pfet_left_ref,3)) + movex(load_pfet_left_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[0])) + remove_ports_with_prefix(opamp,"load_pfet_left_") + opamp.add_ports(load_pfet_left_ref.get_ports_list(),prefix="load_pfet_left_") + # move load_pfet_left north mydiff + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiff_ref,2) + center_to_edge_distance(load_pfet_left_ref,4)) + movey(load_pfet_left_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[1])) + remove_ports_with_prefix(opamp,"load_pfet_left_") + opamp.add_ports(load_pfet_left_ref.get_ports_list(),prefix="load_pfet_left_") + # move load_pfet_left left mydiffload + relativemovcorrection_0 = -1*(maxmetalsep + center_to_edge_distance(mydiffload_ref,1) + center_to_edge_distance(load_pfet_left_ref,3)) + movex(load_pfet_left_ref,destination=(relativemovcorrection_0 + mydiffload_ref.center[0])) + remove_ports_with_prefix(opamp,"load_pfet_left_") + opamp.add_ports(load_pfet_left_ref.get_ports_list(),prefix="load_pfet_left_") + # placing load_pfet_right centered at the origin + load_pfet_right = pmos(pdk,**{'width': load_pfet_width, 'fingers': load_pfet_fingers, 'multipliers': load_pfet_multipliers}) + load_pfet_right_ref = prec_ref_center(load_pfet_right) + opamp.add(load_pfet_right_ref) + opamp.add_ports(load_pfet_right_ref.get_ports_list(),prefix="load_pfet_right_") + # move load_pfet_right right mydiff + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiff_ref,3) + center_to_edge_distance(load_pfet_right_ref,1)) + movex(load_pfet_right_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[0])) + remove_ports_with_prefix(opamp,"load_pfet_right_") + opamp.add_ports(load_pfet_right_ref.get_ports_list(),prefix="load_pfet_right_") + # move load_pfet_right north mydiff + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiff_ref,2) + center_to_edge_distance(load_pfet_right_ref,4)) + movey(load_pfet_right_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[1])) + remove_ports_with_prefix(opamp,"load_pfet_right_") + opamp.add_ports(load_pfet_right_ref.get_ports_list(),prefix="load_pfet_right_") + # move load_pfet_right right mydiffload + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiffload_ref,3) + center_to_edge_distance(load_pfet_right_ref,1)) + movex(load_pfet_right_ref,destination=(relativemovcorrection_0 + mydiffload_ref.center[0])) + remove_ports_with_prefix(opamp,"load_pfet_right_") + opamp.add_ports(load_pfet_right_ref.get_ports_list(),prefix="load_pfet_right_") + opamp << c_route(pdk,opamp.ports["load_pfet_right_multiplier_0_gate_con_N"],opamp.ports["load_pfet_left_multiplier_0_gate_con_N"],**{'extension': 18, 'width1': 0.8, 'width2': 0.8}) + opamp << c_route(pdk,opamp.ports["load_pfet_right_multiplier_0_drain_con_N"],opamp.ports["load_pfet_left_multiplier_0_drain_con_N"],**{'extension': 8, 'width1': 0.8, 'width2': 0.8}) + opamp << c_route(pdk,opamp.ports["load_pfet_right_multiplier_0_source_con_N"],opamp.ports["load_pfet_left_multiplier_0_source_con_N"],**{'extension': 5.5, 'viaoffset': ( True , False ), 'fullbottom': True, 'width1': 0.8, 'width2': 0.8}) + # placing load_curr_source_ref centered at the origin + load_curr_source_ref = nmos(pdk,**{'width': load_curr_source_width_ref, 'fingers': load_curr_source_fingers, 'multipliers': 2}) + load_curr_source_ref_ref = prec_ref_center(load_curr_source_ref) + opamp.add(load_curr_source_ref_ref) + opamp.add_ports(load_curr_source_ref_ref.get_ports_list(),prefix="load_curr_source_ref_") + # move load_curr_source left mydiff + relativemovcorrection_0 = -1*(maxmetalsep + center_to_edge_distance(mydiff_ref,1) + center_to_edge_distance(load_curr_source_ref,3)) + movex(load_curr_source_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[0])) + remove_ports_with_prefix(opamp,"load_curr_source_") + opamp.add_ports(load_curr_source_ref.get_ports_list(),prefix="load_curr_source_") + # move load_curr_source_ref south mydiff + relativemovcorrection_0 = -1*(maxmetalsep + center_to_edge_distance(mydiff_ref,4) + center_to_edge_distance(load_curr_source_ref_ref,2)) + movey(load_curr_source_ref_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[1])) + remove_ports_with_prefix(opamp,"load_curr_source_ref_") + opamp.add_ports(load_curr_source_ref_ref.get_ports_list(),prefix="load_curr_source_ref_") + # move load_curr_source_ref left mycurrmirror + relativemovcorrection_0 = -1*(maxmetalsep + center_to_edge_distance(mycurrmirror_ref,1) + center_to_edge_distance(load_curr_source_ref_ref,3)) + movex(load_curr_source_ref_ref,destination=(relativemovcorrection_0 + mycurrmirror_ref.center[0])) + remove_ports_with_prefix(opamp,"load_curr_source_ref_") + opamp.add_ports(load_curr_source_ref_ref.get_ports_list(),prefix="load_curr_source_ref_") + # placing load_curr_source_mirr centered at the origin + load_curr_source_mirr = nmos(pdk,**{'width': load_curr_source_width_mirr, 'fingers': load_curr_source_fingers, 'multipliers': 2}) + load_curr_source_mirr_ref = prec_ref_center(load_curr_source_mirr) + opamp.add(load_curr_source_mirr_ref) + opamp.add_ports(load_curr_source_mirr_ref.get_ports_list(),prefix="load_curr_source_mirr_") + # move load_curr_source_mirr right mydiff + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiff_ref,3) + center_to_edge_distance(load_curr_source_mirr_ref,1)) + movex(load_curr_source_mirr_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[0])) + remove_ports_with_prefix(opamp,"load_curr_source_mirr_") + opamp.add_ports(load_curr_source_mirr_ref.get_ports_list(),prefix="load_curr_source_mirr_") + # move load_curr_source_mirr south mydiff + relativemovcorrection_0 = -1*(maxmetalsep + center_to_edge_distance(mydiff_ref,4) + center_to_edge_distance(load_curr_source_mirr_ref,2)) + movey(load_curr_source_mirr_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[1])) + remove_ports_with_prefix(opamp,"load_curr_source_mirr_") + opamp.add_ports(load_curr_source_mirr_ref.get_ports_list(),prefix="load_curr_source_mirr_") + # move load_curr_source_mirr right mycurrmirror + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mycurrmirror_ref,3) + center_to_edge_distance(load_curr_source_mirr_ref,1)) + movex(load_curr_source_mirr_ref,destination=(relativemovcorrection_0 + mycurrmirror_ref.center[0])) + remove_ports_with_prefix(opamp,"load_curr_source_mirr_") + opamp.add_ports(load_curr_source_mirr_ref.get_ports_list(),prefix="load_curr_source_mirr_") + opamp << c_route(pdk,opamp.ports["load_curr_source_ref_multiplier_0_gate_con_S"],opamp.ports["load_curr_source_mirr_multiplier_0_gate_con_S"],**{'extension': 3, 'width1': 0.8, 'width2': 0.8}) + opamp << c_route(pdk,opamp.ports["load_curr_source_ref_multiplier_0_drain_con_S"],opamp.ports["load_curr_source_mirr_multiplier_0_drain_con_S"],**{'extension': 12, 'viaoffset': ( False , True ), 'width1': 0.8, 'width2': 0.8}) + opamp << c_route(pdk,opamp.ports["load_curr_source_ref_multiplier_0_source_con_S"],opamp.ports["load_curr_source_mirr_multiplier_0_source_con_S"],**{'extension': 14, 'width1': 0.8, 'width2': 0.8}) + opamp << c_route(pdk,opamp.ports["load_curr_source_ref_multiplier_0_gate_E"],opamp.ports["load_curr_source_ref_multiplier_0_drain_E"],**{'extension': 3}) + opamp << L_route(pdk,opamp.ports["mydiff_tr_multiplier_0_drain_W"],opamp.ports["load_pfet_right_multiplier_0_gate_con_S"],**{}) + opamp << L_route(pdk,opamp.ports["load_pfet_right_multiplier_0_drain_con_S"],opamp.ports["load_curr_source_mirr_multiplier_0_drain_W"],**{}) + # placing load_miller_cap centered at the origin + load_miller_cap = mimcap_array(pdk,**{'rows': mim_cap_rows, 'columns': 2, 'size': mim_cap_size}) + load_miller_cap_ref = prec_ref_center(load_miller_cap) + opamp.add(load_miller_cap_ref) + opamp.add_ports(load_miller_cap_ref.get_ports_list(),prefix="load_miller_cap_") + # move load_miller_cap right mydiff + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiff_ref,3) + center_to_edge_distance(load_miller_cap_ref,1)) + movex(load_miller_cap_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[0])) + remove_ports_with_prefix(opamp,"load_miller_cap_") + opamp.add_ports(load_miller_cap_ref.get_ports_list(),prefix="load_miller_cap_") + # move load_miller_cap north mydiff + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(mydiff_ref,2) + center_to_edge_distance(load_miller_cap_ref,4)) + movey(load_miller_cap_ref,destination=(relativemovcorrection_0 + mydiff_ref.center[1])) + remove_ports_with_prefix(opamp,"load_miller_cap_") + opamp.add_ports(load_miller_cap_ref.get_ports_list(),prefix="load_miller_cap_") + # move load_miller_cap right load_pfet_right + relativemovcorrection_0 = 1*(maxmetalsep + center_to_edge_distance(load_pfet_right_ref,3) + center_to_edge_distance(load_miller_cap_ref,1)) + movex(load_miller_cap_ref,destination=(relativemovcorrection_0 + load_pfet_right_ref.center[0])) + remove_ports_with_prefix(opamp,"load_miller_cap_") + opamp.add_ports(load_miller_cap_ref.get_ports_list(),prefix="load_miller_cap_") + opamp << c_route(pdk,opamp.ports["mydiffload_top_B_0_drain_N"],opamp.ports["load_miller_cap_row0_col0_top_met_N"],**{'extension': 15}) + opamp << c_route(pdk,opamp.ports["load_pfet_right_multiplier_0_drain_con_S"],opamp.ports["load_miller_cap_row0_col0_top_met_S"],**{'width1': 1.2, 'width2': 1.2}) + return opamp diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/opamp_twostage.py b/openfasoc/generators/glayout/glayout/flow/components/opamp_twostage.py similarity index 89% rename from openfasoc/generators/gdsfactory-gen/glayout/components/opamp_twostage.py rename to openfasoc/generators/glayout/glayout/flow/components/opamp_twostage.py index 30fc215dd..8ff5a59c4 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/opamp_twostage.py +++ b/openfasoc/generators/glayout/glayout/flow/components/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.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.components.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 glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.components.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 import c_route +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.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.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.routing.straight_route import straight_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +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 pydantic import validate_arguments -from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized - -from glayout.components.diff_pair_cmirrorbias import diff_pair_ibias -from glayout.components.stacked_current_mirror import stacked_nfet_current_mirror -from glayout.components.differential_to_single_ended_converter import differential_to_single_ended_converter -from glayout.components.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter -from glayout.components.diff_pair_stackedcmirror import diff_pair_stackedcmirror -from glayout.spice import Netlist -from glayout.components.stacked_current_mirror import current_mirror_netlist +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized + +from glayout.flow.components.diff_pair_cmirrorbias import diff_pair_ibias +from glayout.flow.components.stacked_current_mirror import stacked_nfet_current_mirror +from glayout.flow.components.differential_to_single_ended_converter import differential_to_single_ended_converter +from glayout.flow.components.row_csamplifier_diff_to_single_ended_converter import row_csamplifier_diff_to_single_ended_converter +from glayout.flow.components.diff_pair_stackedcmirror import diff_pair_stackedcmirror +from glayout.flow.spice import Netlist +from glayout.flow.components.stacked_current_mirror import current_mirror_netlist @validate_arguments def __create_and_route_pins( diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/row_csamplifier_diff_to_single_ended_converter.py b/openfasoc/generators/glayout/glayout/flow/components/row_csamplifier_diff_to_single_ended_converter.py similarity index 79% rename from openfasoc/generators/gdsfactory-gen/glayout/components/row_csamplifier_diff_to_single_ended_converter.py rename to openfasoc/generators/glayout/glayout/flow/components/row_csamplifier_diff_to_single_ended_converter.py index 0299c6baf..6d6a0df4a 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/row_csamplifier_diff_to_single_ended_converter.py +++ b/openfasoc/generators/glayout/glayout/flow/components/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.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.components.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 glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.components.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 import c_route +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.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.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.routing.straight_route import straight_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +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 pydantic import validate_arguments -from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.spice import Netlist +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.spice import Netlist def row_csamplifier_diff_to_single_ended_converter_netlist(diff_to_single: Component) -> Netlist: overall_netlist = Netlist( diff --git a/openfasoc/generators/gdsfactory-gen/glayout/components/stacked_current_mirror.py b/openfasoc/generators/glayout/glayout/flow/components/stacked_current_mirror.py similarity index 67% rename from openfasoc/generators/gdsfactory-gen/glayout/components/stacked_current_mirror.py rename to openfasoc/generators/glayout/glayout/flow/components/stacked_current_mirror.py index 1470c1c35..961868f86 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/components/stacked_current_mirror.py +++ b/openfasoc/generators/glayout/glayout/flow/components/stacked_current_mirror.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.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.components.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 glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.components.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 import c_route +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.routing.route_quad import route_quad -from glayout.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.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.routing.straight_route import straight_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +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 pydantic import validate_arguments -from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized -from glayout.spice import Netlist +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized +from glayout.flow.spice import Netlist def current_mirror_netlist(pdk: MappedPDK, width: float, length: float, multipliers: int) -> Netlist: return Netlist( diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/__init__.py b/openfasoc/generators/glayout/glayout/flow/pdk/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/util/__init__.py rename to openfasoc/generators/glayout/glayout/flow/pdk/__init__.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/__init__.py b/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/__init__.py similarity index 53% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/__init__.py rename to openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/__init__.py index 20dc68d90..b118bf1c1 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/__init__.py +++ b/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/__init__.py @@ -2,4 +2,4 @@ Usage at the package level: from pdk.gf180_mapped import gf180_mapped_pdk """ -from glayout.pdk.gf180_mapped.gf180_mapped import gf180_mapped_pdk +from glayout.flow.pdk.gf180_mapped.gf180_mapped import gf180_mapped_pdk diff --git a/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/gf180_mapped.py b/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/gf180_mapped.py new file mode 100644 index 000000000..4bffe5e12 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/gf180_mapped.py @@ -0,0 +1,91 @@ +""" +usage: from gf180_mapped import gf180_mapped_pdk +""" + +from ..gf180_mapped.grules import grulesobj +from ..mappedpdk import MappedPDK, SetupPDKFiles +from pathlib import Path + + +#LAYER["fusetop"]=(75, 0) +LAYER = { + "metal5": (81, 0), + "via4": (41, 0), + "metal4": (46, 0), + "via3": (40, 0), + "metal3": (42, 0), + "via2": (38, 0), + "metal2": (36, 0), + "via1": (35, 0), + "metal1": (34, 0), + "contact": (33, 0), + "poly2": (30, 0), + "comp": (22, 0), + "nplus": (32, 0), + "pplus": (31, 0), + "nwell": (21, 0), + "lvpwell": (204, 0), + "dnwell": (12, 0), + "CAP_MK": (117, 5) +} + +gf180_glayer_mapping = { + "met5": "metal5", + "via4": "via4", + "met4": "metal4", + "via3": "via3", + "met3": "metal3", + "via2": "via2", + "met2": "metal2", + "via1": "via1", + "met1": "metal1", + "mcon": "contact", + "poly": "poly2", + "active_diff": "comp", + "active_tap": "comp", + "n+s/d": "nplus", + "p+s/d": "pplus", + "nwell": "nwell", + "pwell": "lvpwell", + "dnwell": "dnwell", + "capmet": "CAP_MK" +} + +# note for DRC, there is mim_option 'A'. This is the one configured for use + +gf180_lydrc_file_path = Path(__file__).resolve().parent / "gf180mcu_drc.lydrc" + +openfasoc_dir = Path(__file__).resolve().parent.parent.parent.parent.parent.parent.parent +pdk_root = Path('/usr/bin/miniconda3/share/pdk/') +lvs_schematic_ref_file = openfasoc_dir / "common" / "platforms" / "gf180osu9t" / "cdl" / "gf180mcu_osu_sc_9T.spice" +magic_drc_file = pdk_root / "gf180mcuC" / "libs.tech" / "magic" / "gf180mcuC.magicrc" +lvs_setup_tcl_file = pdk_root / "gf180mcuC" / "libs.tech" / "netgen" / "gf180mcuC_setup.tcl" +temp_dir = None + + +pdk_files = SetupPDKFiles( + pdk_root=pdk_root, + klayout_drc_file=gf180_lydrc_file_path, + lvs_schematic_ref_file=lvs_schematic_ref_file, + lvs_setup_tcl_file=lvs_setup_tcl_file, + magic_drc_file=magic_drc_file, + temp_dir=temp_dir, + pdk='gf180' +).return_dict_of_files() + +gf180_mapped_pdk = MappedPDK( + name="gf180", + glayers=gf180_glayer_mapping, + models={ + 'nfet': 'nfet_03v3', + 'pfet': 'pfet_03v3', + 'mimcap': 'mimcap_1p0fF' + }, + layers=LAYER, + pdk_files=pdk_files, + grules=grulesobj, +) + +# configure the grid size and other settings +gf180_mapped_pdk.gds_write_settings.precision = 5*10**-9 +gf180_mapped_pdk.cell_decorator_settings.cache=False diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/gf180mcu.drc b/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/gf180mcu.drc similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/gf180mcu.drc rename to openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/gf180mcu.drc diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/gf180mcu_drc.lydrc b/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/gf180mcu_drc.lydrc similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/gf180mcu_drc.lydrc rename to openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/gf180mcu_drc.lydrc diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/grules.py b/openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/grules.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/gf180_mapped/grules.py rename to openfasoc/generators/glayout/glayout/flow/pdk/gf180_mapped/grules.py diff --git a/openfasoc/generators/glayout/glayout/flow/pdk/mappedpdk.py b/openfasoc/generators/glayout/glayout/flow/pdk/mappedpdk.py new file mode 100644 index 000000000..878f19354 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/pdk/mappedpdk.py @@ -0,0 +1,898 @@ +""" +usage: from mappedpdk import MappedPDK +""" + +from gdsfactory.pdk import Pdk +from gdsfactory.typings import Component, PathType, Layer +from pydantic import validator, StrictStr, ValidationError +from typing import ClassVar, Optional, Any, Union, Literal, Iterable, TypedDict +from pathlib import Path +from decimal import Decimal, ROUND_UP +import tempfile +import subprocess +from decimal import Decimal +from pydantic import validate_arguments +import xml.etree.ElementTree as ET +import pathlib, shutil, os, sys + +class SetupPDKFiles: + """Class to setup the PDK files required for DRC and LVS checks. + """ + + def __init__( + self, + pdk_root: Optional[PathType] = None, + klayout_drc_file: Optional[PathType] = None, + lvs_schematic_ref_file: Optional[PathType] = None, + lvs_setup_tcl_file: Optional[PathType] = None, + magic_drc_file: Optional[PathType] = None, + temp_dir: Optional[PathType] = None, + pdk: Optional[str] = 'sky130' + ): + """Initializes the class with the required PDK files for DRC and LVS checks.""" + self.pdk = pdk + self.temp_dir = temp_dir + + if pdk_root is None: + # condition for pdk_root not being provided + if klayout_drc_file is None: + raise ValueError("Please provide a Klayout DRC file if not providing PDK root!") + else: + if not isinstance(klayout_drc_file, PathType): + raise ValueError("The file must be provided as a Path object or string") + else: + self.klayout_drc_file = klayout_drc_file + if magic_drc_file is None: + raise ValueError("Please provide a magic DRC file") + else: + if not isinstance(magic_drc_file, PathType): + raise ValueError("The files must be provided as Path objects or strings") + else: + self.magic_drc_file = magic_drc_file + if lvs_schematic_ref_file is None or lvs_setup_tcl_file is None or magic_drc_file is None: + raise ValueError(f"""Please provide the following files: + - a spice file with subckt references(lvs_schematic_ref_file) + - a tcl file with setup commands(lvs_setup_tcl_file) + - a magic DRC file(magic_drc_file)""") + else: + if not isinstance(lvs_schematic_ref_file, PathType) or not isinstance(lvs_setup_tcl_file, PathType): + raise ValueError("The files must be provided as Path objects or strings") + else: + self.lvs_schematic_ref_file = lvs_schematic_ref_file + self.lvs_setup_tcl_file = lvs_setup_tcl_file + self.magic_drc_file = magic_drc_file + else: + if not isinstance(pdk_root, PathType): + # condition for pdk_root not being a Path object + raise ValueError("pdk_root must be a Path object or string") + # condition for pdk_root being provided + self.pdk_root = pdk_root + if klayout_drc_file is None: + # condition for klayout_drc_file not being provided with pdk_root provided + if pdk == 'sky130': + klayout_drc_file = Path(pdk_root) / "sky130A" / "libs.tech" / "klayout" / "drc" / "sky130.lydrc" + elif pdk == 'gf180': + raise NotImplementedError("Klayout lvs is not supported for gf180 PDK") + else: + raise ValueError("pdk must be either 'sky130' or 'gf180', others not supported!") + + + if lvs_schematic_ref_file is None: + if pdk == 'gf180': + raise NotImplementedError("LVS is not supported for gf180 PDK") + + lvs_spice_file = Path(pdk_root) / "sky130A" / "libs.ref" / "sky130_fd_sc_hd" / "spice" / "sky130_fd_sc_hd.spice" + lvs_schematic_ref_file = temp_dir / "sky130_fd_sc_hd.spice" + self.write_custom_spice_to_file(lvs_spice_file, lvs_schematic_ref_file) + + if lvs_setup_tcl_file is None: + if pdk == 'gf180': + raise NotImplementedError("LVS is not supported for gf180 PDK") + dest_lvs_setup_tcl = temp_dir / "sky130A_setup.tcl" + lvs_setup_tcl_file = self.magic_netgen_file_exists(dest_lvs_setup_tcl, pdk_root) + + if magic_drc_file is None: + if pdk == "sky130": + dest_magic_drc = temp_dir / f"{pdk}A.magicrc" + elif pdk == "gf180": + dest_magic_drc = temp_dir / f"{pdk}mcuC.magicrc" + + magic_drc_file = self.magic_netgen_file_exists(dest_magic_drc, pdk_root) + + self.klayout_drc_file = klayout_drc_file + self.lvs_schematic_ref_file = lvs_schematic_ref_file + self.lvs_setup_tcl_file = lvs_setup_tcl_file + self.magic_drc_file = magic_drc_file + + def write_custom_spice_to_file( + self, + input_spice: str, + output_spice: str + ): + """Writes a custom spice file to the output_spice file path.""" + custom_spice_string = f""" +* HEADER and SLC cells +* Proxy pins that may or may not be generated by magic sky130_fd_sc_hd__tap_1_0/VPB sky130_fd_sc_hd__tap_1_1/VPB + +.subckt HEADER VGND VIN VNB VPWR +xMN0 VPWR VGND net7 VNB sky130_fd_pr__nfet_03v3_nvt ad=0p pd=0u as=0p ps=0u w=700000u l=500000u +xMN1 net7 VGND VPWR VNB sky130_fd_pr__nfet_03v3_nvt ad=1.1445e+12p pd=1.167e+07u as=3.92e+11p ps=3.92e+06u w=700000u l=500000u +xMN2 VPWR VGND net7 VNB sky130_fd_pr__nfet_03v3_nvt ad=0p pd=0u as=0p ps=0u w=700000u l=500000u +xMN3 net7 VGND VPWR VNB sky130_fd_pr__nfet_03v3_nvt ad=0p pd=0u as=0p ps=0u w=700000u l=500000u +xMN4 net7 VGND VIN VNB sky130_fd_pr__nfet_03v3_nvt ad=0p pd=0u as=0p ps=0u w=700000u l=500000u +xMN5 VIN VGND net7 VNB sky130_fd_pr__nfet_03v3_nvt ad=3.92e+11p pd=3.92e+06u as=0p ps=0u w=700000u l=500000u +xMN6 net7 VGND VIN VNB sky130_fd_pr__nfet_03v3_nvt ad=0p pd=0u as=0p ps=0u w=700000u l=500000u +xMN7 VIN VGND net7 VNB sky130_fd_pr__nfet_03v3_nvt ad=0p pd=0u as=0p ps=0u w=700000u l=500000u +.ends + +.subckt SLC IN INB VOUT VGND VNB VPWR VPB +xMP0 net02 net07 VPWR VPB sky130_fd_pr__pfet_01v8_hvt m=1 mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +xMP1 net03 net03 net02 VPB sky130_fd_pr__pfet_01v8_hvt mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +xMP3 net06 net03 VPWR VPB sky130_fd_pr__pfet_01v8_hvt m=1 mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +xMP4 net07 net07 net06 VPB sky130_fd_pr__pfet_01v8_hvt m=1 mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +xMP6 VOUT net06 VPWR VPB sky130_fd_pr__pfet_01v8_hvt m=2 mult=1 w=0.5 l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +xMN4 VOUT net07 VGND VNB sky130_fd_pr__nfet_01v8 m=1 mult=1 w=0.65 l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +xMN0 net03 INB VGND VNB sky130_fd_pr__nfet_01v8_lvt m=10 mult=1 w=0.5 l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +xMN8 net07 IN VGND VNB sky130_fd_pr__nfet_01v8_lvt m=10 mult=1 w=500000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +.ends + +******* EOF + +* Modified subthreshold inverter cell + +.subckt dinv1 Yb Y Ab A Apb Ap VGND VPWR +X1 t1 Apb VPWR VPWR sky130_fd_pr__pfet_01v8_hvt m=1 mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +X2 Y Yb t1 t1 sky130_fd_pr__pfet_01v8_hvt m=1 mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +X3 t2 Ap VPWR VPWR sky130_fd_pr__pfet_01v8_hvt m=1 mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +X4 Yb Y t2 t2 sky130_fd_pr__pfet_01v8_hvt m=1 mult=1 w=360000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +X5 Y Ab t3 t3 sky130_fd_pr__nfet_01v8 m=1 mult=1 w=180000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +X6 t3 Ab VGND VGND sky130_fd_pr__nfet_01v8 m=1 mult=1 w=180000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +X7 Yb A t4 t4 sky130_fd_pr__nfet_01v8 m=1 mult=1 w=180000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +X8 t4 A VGND VGND sky130_fd_pr__nfet_01v8 m=1 mult=1 w=180000u l=150000u sa=265e-3 sb=265e-3 sd=280e-3 +.ends + +******** EOF +""" + with open(input_spice, 'r') as rf: + lines = rf.readlines() + with open(output_spice, 'w') as wf: + for line in lines: + wf.write(line) + wf.write(custom_spice_string) + + def magic_netgen_file_exists( + self, + magic_file, + pdk_root + ): + """Check that magic_file exists if not none, and copy it to the required pdk directory if it doesn't exist""" + def copy_if_not_exists(src, dest): + if not os.path.exists(dest): + print(f'copying file from required pdk dir: {src}') + shutil.copy(src, dest) + else: + print(f'Either file not found: {src}, or already copied to: {dest}') + + def return_pdk_full_name(self): + """Returns the full name of the pdk based on the class name""" + if self.pdk == 'sky130': + return f"{self.pdk}A" + elif self.pdk == 'gf180': + return f"{self.pdk}mcuC" + + # Check if the magic_file exists, if not, copy it to the required pdk directory + if magic_file != None and not magic_file.is_file(): + # Check if the pdk_root is a directory, if not raise an error + if pdk_root != None and Path(pdk_root).resolve().is_dir(): + pdk_name = return_pdk_full_name(self) + if ".magicrc" in str(magic_file): + magic_file_source = Path(pdk_root) / f"{pdk_name}" / "libs.tech" / "magic" / f"{pdk_name}.magicrc" + elif "_setup.tcl" in str(magic_file): + magic_file_source = Path(pdk_root) / f"{pdk_name}" / "libs.tech" / "netgen" / f"{pdk_name}_setup.tcl" + copy_if_not_exists(magic_file_source, magic_file) + return magic_file + elif pdk_root != None and not pdk_root.is_dir(): + raise ValueError("pdk_root must be a directory") + raise ValueError("magic/netgen script: the path given is not a file") + return magic_file + + def return_dict_of_files( + self + ): + pdk_files = { + 'pdk_root': self.pdk_root, + 'klayout_drc_file': self.klayout_drc_file, + 'lvs_schematic_ref_file': self.lvs_schematic_ref_file, + 'lvs_setup_tcl_file': self.lvs_setup_tcl_file, + 'magic_drc_file': self.magic_drc_file, + 'temp_dir': self.temp_dir, + 'pdk': self.pdk + } + return pdk_files + + +class MappedPDK(Pdk): + """Inherits everything from the pdk class but also requires mapping to glayers + glayers are generic layers which can be returned with get_glayer(name: str) + has_required_glayers(list[str]) is used to verify all required generic layers are + present""" + + valid_glayers: ClassVar[tuple[str]] = ( + "dnwell", + "pwell", + "nwell", + "p+s/d", + "n+s/d", + "active_diff", + "active_tap", + "poly", + "mcon", + "met1", + "via1", + "met2", + "via2", + "met3", + "via3", + "met4", + "via4", + "met5", + "capmet", + ) + + models: dict = { + "nfet": "", + "pfet": "", + "mimcap": "" + } + + glayers: dict[StrictStr, Union[StrictStr, tuple[int,int]]] + # friendly way to implement a graph + grules: dict[StrictStr, dict[StrictStr, Optional[dict[StrictStr, Any]]]] + pdk_files: dict[StrictStr, Union[PathType, None]] + + @validator("models") + def models_check(cls, models_obj: dict[StrictStr, StrictStr]): + for model in models_obj.keys(): + if not model in ["nfet","pfet","mimcap"]: + raise ValueError(f"specify nfet, pfet, or mimcap models only") + return models_obj + + @validator("glayers") + def glayers_check_keys(cls, glayers_obj: dict[StrictStr, Union[StrictStr, tuple[int,int]]]): + """force people to pick glayers from a finite set of string layers that you define + checks glayers to ensure valid keys,type. Glayers must be passed as dict[str,str] + if someone tries to pass a glayers dict that has a bad key, throw an error""" + for glayer, mapped_layer in glayers_obj.items(): + if (not isinstance(glayer, str)) or (not isinstance(mapped_layer, Union[str, tuple])): + raise TypeError("glayers should be passed as dict[str, Union[StrictStr, tuple[int,int]]]") + if glayer not in cls.valid_glayers: + raise ValueError( + "glayers keys must be one of generic layers listed in class variable valid_glayers" + ) + return glayers_obj + + @validate_arguments + def drc( + self, + layout: Component | PathType, + output_dir_or_file: Optional[PathType] = None, + ): + """Returns true if the layout is DRC clean and false if not + Also saves detailed results to output_dir_or_file location as lyrdb + layout can be passed as a file path or gdsfactory component""" + if not self.klayout_lydrc_file: + raise NotImplementedError("no drc script for this pdk") + # find layout gds file path + tempdir = None + if isinstance(layout, Component): + tempdir = tempfile.TemporaryDirectory() + layout_path = Path(layout.write_gds(gdsdir=tempdir.name)).resolve() + elif isinstance(layout, PathType): + layout_path = Path(layout).resolve() + else: + raise TypeError("layout should be a Component, Path, or string") + if not layout_path.is_file(): + raise ValueError("layout must exist, the path given is not a file") + # find report file path, if None then use current directory + report_path = ( + Path(output_dir_or_file).resolve() + if output_dir_or_file + else Path.cwd().resolve() + ) + if report_path.is_dir(): + report_path = Path( + report_path + / str( + self.name + + layout_path.name.replace(layout_path.suffix, "") + + "_drcreport.lyrdb" + ) + ) + elif not report_path.is_file(): + raise ValueError("report_path must be file or dir") + # run klayout drc + drc_args = [ + "klayout", + "-b", + "-r", + str(self.pdk_files['klayout_lydrc_file']), + "-rd", + "input=" + str(layout_path), + "-rd", + "report=" + str(report_path), + ] + rtr_code = subprocess.Popen(drc_args).wait() + if rtr_code: + raise RuntimeError("error running klayout DRC") + # clean up and return + if tempdir: + tempdir.cleanup() + # there is a drc parsing open-source at: + # https://github.com/google/globalfoundries-pdk-libs-gf180mcu_fd_pr/blob/main/rules/klayout/drc + # eventually I can return more info on the drc run, but for now just void and view the lyrdb in klayout + + # Open DRC output XML file + drc_tree = ET.parse(report_path.resolve()) + drc_root = drc_tree.getroot() + if drc_root.tag != "report-database": + raise TypeError("DRC report file is not a valid report-database") + # Check if DRC passed + drc_error_count = len(drc_root[7]) + return (drc_error_count == 0) + + @validate_arguments + def drc_magic( + self, + layout: Component | str, + design_name: str, + pdk_root: Optional[PathType] = None, + magic_drc_file: Optional[PathType] = None + ): + """Runs DRC using magic on the either the component or the gds file path provided. Requires the design name and the pdk_root to be specified, handles importing the required magicrc and other setup files, if not specified. Accepts overriden magic_commands_file and magic_drc_file. + + Args: + - layout (Component | str): + - Either the Component or the gds file path to run DRC on + - design_name (str): + - The designated name of the design + - pdk_root (Optional[PathType], optional): + - The directory where the pdk files are located. + - e.g. - "/usr/bin/miniconda3/share/pdk/". Defaults to "/usr/bin/miniconda3/share/pdk/". + - magic_drc_file (Optional[PathType], optional): + - The .magicrc file for your PDK of choice. + - Defaults to None. + + Raises: + - ValueError: + - If the magic DRC cannot be run! + - Please either provide a PDK root or the following files: + - a file containing magic commands to be executed for DRC (magic_commands.tcl) + - the .magicrc file for your PDK of choice + """ + + def create_magic_commands_file(temp_dir): + # magic commands file creation + print("Defaulting to stale magic_commands.tcl") + magic_commands_file_str = f""" +gds flatglob *$$* +gds flatglob *VIA* +gds flatglob *CDNS* +gds flatglob *capacitor_test_nf* + +gds read $::env(RESULTS_DIR)/$::env(DESIGN_NAME).gds + +proc custom_drc_save_report {{{{cellname ""}} {{outfile ""}}}} {{ + +if {{$outfile == ""}} {{set outfile "drc.out"}} + +set fout [open $outfile w] +set oscale [cif scale out] +if {{$cellname == ""}} {{ + select top cell + set cellname [cellname list self] + set origname "" +}} else {{ + set origname [cellname list self] + puts stdout "\[INFO\]: Loading $cellname\n" + flush stdout + + load $cellname + select top cell +}} + +drc check +set count [drc list count] + +puts $fout "$cellname count: $count" +puts $fout "----------------------------------------" +set drcresult [drc listall why] +foreach {{errtype coordlist}} $drcresult {{ + puts $fout $errtype + puts $fout "----------------------------------------" + foreach coord $coordlist {{ + set bllx [expr {{$oscale * [lindex $coord 0]}}] + set blly [expr {{$oscale * [lindex $coord 1]}}] + set burx [expr {{$oscale * [lindex $coord 2]}}] + set bury [expr {{$oscale * [lindex $coord 3]}}] + set coords [format " %.3fum %.3fum %.3fum %.3fum" $bllx $blly $burx $bury] + puts $fout "$coords" + }} +puts $fout "----------------------------------------" +}} +puts $fout "" + +if {{$origname != ""}} {{ + load $origname +}} + +close $fout +puts "\[INFO\]: DONE with $outfile\n" +}} + +custom_drc_save_report $::env(DESIGN_NAME) $::env(REPORTS_DIR)/$::env(DESIGN_NAME).rpt +""" + + new_path = temp_dir / "magic_commands.tcl" + with open(str(new_path.resolve()), 'w') as f: + f.write(magic_commands_file_str) + + return new_path.resolve() + + self.pdk_files['pdk'] = self.name + with tempfile.TemporaryDirectory() as temp_dir: + temp_dir_path = pathlib.Path(temp_dir).resolve() + self.pdk_files['temp_dir'] = temp_dir_path + + if pdk_root is None: + print("using default pdk_root: /usr/bin/miniconda3/share/pdk/") + else: + print('using provided pdk_root') + self.pdk_files['pdk_root'] = pdk_root + + env_vars = { + 'PDK_ROOT': str(self.pdk_files['pdk_root']), + 'DESIGN_NAME': design_name, + 'REPORTS_DIR': str(temp_dir_path), + 'RESULTS_DIR': str(temp_dir_path) + } + os.environ.update(env_vars) + + gds_path = str(temp_dir_path / f"{design_name}.gds") + if isinstance(layout, Component): + layout.write_gds(gds_path) + elif isinstance(layout, PathType): + shutil.copy(layout, gds_path) + + magicrc_file = self.pdk_files['magic_drc_file'] if magic_drc_file is None else magic_drc_file + magic_cmd_file = create_magic_commands_file(temp_dir_path) + cmd = f'bash -c "magic -rcfile {magicrc_file} -noconsole -dnull {magic_cmd_file} < /dev/null"' + + subp = subprocess.Popen( + cmd, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + + subp.wait() + print(subp.stdout.read().decode('utf-8')) + + subproc_code = subp.returncode + result_str = "magic drc script passed" if subproc_code == 0 else "magic drc script failed" + # print errors + + errors = subp.stderr.read().decode('utf-8') + if errors: + print(f"Soft errors: \n{errors}") + + with open(f'{str(temp_dir_path)}/{design_name}.rpt', 'r') as f: + num_lines = len(f.readlines()) + if num_lines > 3: + result_str = result_str + "\nErrors found in DRC report" + f.seek(0) + print(f.read()) + else: + result_str = result_str + "\nNo errors found in DRC report" + + print(result_str) + + @validate_arguments + def lvs_netgen( + self, + layout: Component | PathType, + design_name: str, + pdk_root: Optional[PathType] = None, + lvs_setup_tcl_file: Optional[PathType] = None, + lvs_schematic_ref_file: Optional[PathType] = None, + magic_drc_file: Optional[PathType] = None, + netlist: Optional[PathType] = None, + report_handling: Optional[tuple[Optional[bool], Optional[str]]] = (False, None) + ): + """Runs LVS using netgen on the either the component or the gds file path provided. Requires the design name and the pdk_root to be specified, handles importing the required setup files, if not specified. Accepts overriden lvs_setup_tcl_file, lvs_schematic_ref_file, and magic_drc_file. + + Args: + - layout (Component | PathType): + - Either the Component or the gds file path to run LVS on + design_name (str): + - The designated name of the design + pdk_root (Optional[PathType], optional): + - The directory where the pdk files are located. + - e.g. - "/usr/bin/miniconda3/share/pdk/". Defaults to None. + - lvs_setup_tcl_file (Optional[PathType], optional): + - A file containing netgen setup commands to be executed for LVS (sky130A_setup.tcl). + - Defaults to None. + - (Found in libs.tech/netgen/ of the pdk directory) + - lvs_schematic_ref_file (Optional[PathType], optional): + - A spice file containing subckt definitions for the PDK. + - Defaults to None. + - (Found in libs.ref/sky130_fd_sc_hd/spice of the pdk directory) + - magic_drc_file (Optional[PathType], optional): + - The .magicrc file for your PDK of choice. + - Defaults to None. + - netlist (Optional[PathType], optional): + - The path to the CDL file (netlist) for the design. + - Defaults to None. + - report_handling (Optional[tuple[Optional[bool], Optional[str]]], optional): + - A tuple containing a boolean value and a string. + - The boolean value indicates whether to handle the report or not, and the string indicates the path to the report. + - Defaults to (False, None). + + Raises: + - NotImplementedError: + - If LVS is not supported for gf180 PDK + - RuntimeError: + - If Netgen or Magic is not found in the system + - ValueError: + - If LVS cannot be run! + - Please provide either a PDK root or the following files: a setup.tcl file for the PDK, a .magicrc file, and a spice file containing subckt definitions for the PDK. + + """ + if not self.name == 'sky130': + raise NotImplementedError("LVS only supported for sky130 PDK") + def check_if_path_or_net_string(netlist: PathType): + cdl_suffix = ".cdl" + spice_suffix = ".spice" + if netlist is None: + raise ValueError("Path to cdl (netlist) must be provided!") + check_suffix = str(netlist).endswith(cdl_suffix) or str(netlist).endswith(spice_suffix) + return check_suffix # True for cdl and spice, False if net passed + + def extract_design_name_from_netlist(file_path: str): + """ Extracts the design name from the netlist file (found after the final .ends statement in the netlist file)""" + with open(file_path, 'r') as file: + lines = file.readlines() + last_ends_line = None + for line in lines: + if line.strip().startswith(".ends"): + last_ends_line = line.strip() + + if last_ends_line: + parts = last_ends_line.split() + if len(parts) > 1: + return parts[1] + else: + return None + + def check_command_exists(command: str): + """ Check if a command exists in the system """ + result = subprocess.run(f"command -v {command}", shell=True, capture_output=True, text=True) + return result.returncode == 0 + + def modify_design_name_in_cdl(netlist, design_name): + design_name_from_cdl = extract_design_name_from_netlist(netlist) + if design_name_from_cdl is not None and design_name_from_cdl == design_name: + print(f"Design name from CDL file: {design_name_from_cdl} matches the design name: {design_name}") + else: + # replace all occurences of design_name_from_cdl with design_name in the cdl file + with open(netlist, 'r') as file: + filedata = file.read() + newdata = filedata.replace(design_name_from_cdl, design_name) + with open(netlist, 'w') as file: + file.write(newdata) + + def write_spice(input_cdl, output_spice, lvs_schematic_ref_file): + # create {design_name}.spice + with open(input_cdl, 'r') as file: + lines = file.readlines() + with open(output_spice, 'w') as file2: + sky130_spice_path = Path(lvs_schematic_ref_file).resolve() + file2.write(f".include {sky130_spice_path}\n") + # write the rest of the lines + for line in lines: + file2.write(line) + + if not check_command_exists("netgen"): + raise RuntimeError("Netgen not found in the system") + if not check_command_exists("magic"): + raise RuntimeError("Magic not found in the system") + os.environ['DESIGN_NAME'] = design_name + # results go to a tempdirectory + with tempfile.TemporaryDirectory() as temp_dir: + temp_dir_path = Path(temp_dir).resolve() + self.pdk_files['temp_dir'] = temp_dir_path + print("using user specified pdk_root, will search for required files in the specified directory") + self.pdk_files['pdk_root'] = pdk_root + + lvsmag_path = temp_dir_path / f"{design_name}_lvsmag.spice" + pex_path = temp_dir_path / f"{design_name}_pex.spice" + sim_path = temp_dir_path / f"{design_name}_sim.spice" + spice_path = temp_dir_path / f"{design_name}.spice" + netlist_from_comp = temp_dir_path / f"{design_name}.cdl" + gds_path = temp_dir_path / f"{design_name}.gds" + report_path = temp_dir_path / f"{design_name}_lvs.rpt" + + if isinstance(layout, Component): + layout.write_gds(str(gds_path)) + if netlist is None: + netlist = layout.info['netlist'].generate_netlist() + with open(str(netlist_from_comp), 'w') as f: + f.write(netlist) + else: + if check_if_path_or_net_string(netlist): + shutil.copy(netlist, str(netlist_from_comp)) + else: + with open(str(netlist_from_comp), 'w') as f: + f.write(netlist) + elif isinstance(layout, PathType): + shutil.copy(layout, str(gds_path)) + if netlist is None: + raise ValueError("Path to cdl (netlist) must be provided if only gds file is provided! Provide Component alternatively!") + else: + if check_if_path_or_net_string(netlist): + shutil.copy(netlist, str(netlist_from_comp)) + else: + with open(str(netlist_from_comp), 'w') as f: + f.write(netlist) + + modify_design_name_in_cdl(str(netlist_from_comp), design_name) + + lvsschemref_file = self.pdk_files['lvs_schematic_ref_file'] if lvs_schematic_ref_file is None else lvs_schematic_ref_file + + + write_spice(str(netlist_from_comp), str(spice_path), lvsschemref_file) + + magic_script_content = f""" +gds flatglob *\\$\\$* +gds read {gds_path} +load {design_name} + +select top cell +extract all +ext2spice lvs +ext2spice -o {str(lvsmag_path)} +load {design_name} +extract all +ext2spice lvs +ext2spice rthresh 0 +ext2spice cthresh 0 +ext2spice -o {str(pex_path)} +load {design_name} +extract all +ext2spice cthresh 0 +ext2spice -o {str(sim_path)} +exit +""" + + with tempfile.NamedTemporaryFile(mode='w', delete=False) as magic_script_file: + magic_script_file.write(magic_script_content) + magic_script_path = magic_script_file.name + + try: + + magicrc_file = self.pdk_files['magic_drc_file'] if magic_drc_file is None else magic_drc_file + magic_cmd = f"bash -c 'magic -rcfile {magicrc_file} -noconsole -dnull < {magic_script_path}'", + magic_subproc = subprocess.run( + magic_cmd, + shell=True, + check=True, + capture_output=True + ) + + magic_subproc_code = magic_subproc.returncode + magic_subproc_out = magic_subproc.stdout.decode('utf-8') + print(magic_subproc_out) + + lvssetup_file = self.pdk_files['lvs_setup_tcl_file'] if lvs_setup_tcl_file is None else lvs_setup_tcl_file + netgen_command = f'netgen -batch lvs "{str(lvsmag_path)} {design_name}" "{str(spice_path)} {design_name}" {lvssetup_file} {str(report_path)}' + + netgen_subproc = subprocess.run( + netgen_command, + shell=True, + check=True, + capture_output=True + ) + netgen_subproc_code = netgen_subproc.returncode + netgen_subproc_out = netgen_subproc.stdout.decode('utf-8') + print(netgen_subproc_out) + + if netgen_subproc_code == 0 and magic_subproc_code == 0: + print("LVS run succeeded, writing report...") + else: + raise ValueError("LVS run failed") + + finally: + os.remove(magic_script_path) + if os.path.exists(f'{design_name}.ext'): + os.remove(f'{design_name}.ext') + # copy the report from the temp directory to the specified location + if report_handling[0]: + shutil.copy(report_path, report_handling[1]) + + + @validate_arguments + def has_required_glayers(self, layers_required: list[str]): + """Raises ValueError if any of the generic layers in layers_required: list[str] + are not mapped to anything in the pdk.glayers dictionary + also checks that the values in the glayers dictionary map to real Pdk layers""" + for layer in layers_required: + if layer not in self.glayers: + raise ValueError( + f"{layer!r} not in self.glayers {list(self.glayers.keys())}" + ) + if isinstance(self.glayers[layer], str): + self.validate_layers([self.glayers[layer]]) + elif not isinstance(self.glayers[layer], tuple): + raise TypeError("glayer mapped value should be str or tuple[int,int]") + + + @validate_arguments + def layer_to_glayer(self, layer: tuple[int, int]) -> str: + """if layer provided corresponds to a glayer, will return a glayer + else will raise an exception + takes layer as a tuple(int,int)""" + # lambda for finding last matching key in dict from val + find_last = lambda val, d: [x for x, y in d.items() if y == val].pop() + if layer in self.glayers.values(): + return find_last(layer, self.glayers) + elif self.layers is not None: + # find glayer verfying presence along the way + pdk_real_layers = self.layers.values() + if layer in pdk_real_layers: + layer_name = find_last(layer, self.layers) + if layer_name in self.glayers.values(): + glayer_name = find_last(layer_name, self.glayers) + else: + raise ValueError("layer does not correspond to a glayer") + else: + raise ValueError("layer is not a layer present in the pdk") + return glayer_name + else: + raise ValueError("layer might not be a layer present in the pdk") + + # TODO: implement LayerSpec type + @validate_arguments + def get_glayer(self, layer: str) -> Layer: + """Returns the pdk layer from the generic layer name""" + direct_mapping = self.glayers[layer] + if isinstance(direct_mapping, tuple): + return direct_mapping + else: + return self.get_layer(direct_mapping) + + @validate_arguments + def get_grule( + self, glayer1: str, glayer2: Optional[str] = None, return_decimal = False + ) -> dict[StrictStr, Union[float,Decimal]]: + """Returns a dictionary describing the relationship between two layers + If one layer is specified, returns a dictionary with all intra layer rules""" + if glayer1 not in MappedPDK.valid_glayers: + raise ValueError("get_grule, " + str(glayer1) + " not valid glayer") + # decide if two or one inputs and set rules_dict accordingly + rules_dict = None + if glayer2 is not None: + if glayer2 not in MappedPDK.valid_glayers: + raise ValueError("get_grule, " + str(glayer2) + " not valid glayer") + rules_dict = self.grules.get(glayer1, dict()).get(glayer2) + if rules_dict is None or rules_dict == {}: + rules_dict = self.grules.get(glayer2, dict()).get(glayer1) + else: + glayer2 = glayer1 + rules_dict = self.grules.get(glayer1, dict()).get(glayer1) + # error check, convert type, and return + if rules_dict is None or rules_dict == {}: + raise NotImplementedError( + "no rules found between " + str(glayer1) + " and " + str(glayer2) + ) + for rule in rules_dict: + if type(rule) == float and return_decimal: + rules_dict[rule] = Decimal(str(rule)) + return rules_dict + + @classmethod + def is_routable_glayer(cls, glayer: StrictStr): + return any(hint in glayer for hint in ["met", "active", "poly"]) + + # TODO: implement + @classmethod + def from_gf_pdk( + cls, + gfpdk: Pdk, + **kwargs + ): + """Construct a mapped pdk from an existing pdk and the extra parts of MappedPDK + grid is the grid size in nm""" + # input type and value validation + if not isinstance(gfpdk, Pdk): + raise TypeError("from_gf_pdk: gfpdk arg only accepts GDSFactory pdk type") + # create argument dictionary + passargs = dict() + # pdk args + passargs["name"]=gfpdk.name + #passargs["cross_sections"]=gfpdk.cross_sections + #passargs["cells"]=gfpdk.cells + #passargs["symbols"]=gfpdk.symbols + #passargs["default_symbol_factory"]=gfpdk.default_symbol_factory + #passargs["containers"]=gfpdk.containers + #passargs["base_pdk"]=gfpdk.base_pdk + #passargs["default_decorator"]=gfpdk.default_decorator + passargs["layers"]=gfpdk.layers + #passargs["layer_stack"]=gfpdk.layer_stack + #passargs["layer_views"]=gfpdk.layer_views#??? layer view broken??? +# passargs["layer_transitions"]=gfpdk.layer_transitions +# passargs["sparameters_path"]=gfpdk.sparameters_path +# passargs["modes_path"]=gfpdk.modes_path +# passargs["interconnect_cml_path"]=gfpdk.interconnect_cml_path +# passargs["warn_off_grid_ports"]=gfpdk.warn_off_grid_ports +# passargs["constants"]=gfpdk.constants +# passargs["materials_index"]=gfpdk.materials_index +# passargs["routing_strategies"]=gfpdk.routing_strategies +# passargs["circuit_yaml_parser"]=gfpdk.circuit_yaml_parser +# passargs["gds_write_settings"]=gfpdk.gds_write_settings +# passargs["oasis_settings"]=gfpdk.oasis_settings +# passargs["cell_decorator_settings"]=gfpdk.cell_decorator_settings +# passargs["bend_points_distance"]=gfpdk.bend_points_distance + # MappedPDK args override existing args + passargs.update(kwargs) + # create and return MappedPDK + mappedpdk = MappedPDK(**passargs) + return mappedpdk + + # util methods + @validate_arguments + def util_max_metal_seperation(self, metal_levels: Union[list[int],list[str], str, int] = range(1,6)) -> float: + """returns the maximum of the min_seperation rule for all layers specfied + although the name of this function is util_max_metal_seperation, layers do not have to be metals + you can specify non metals by using metal_levels=list of glayers + if metal_levels is list of int, integers are converted to metal levels + if a single int is provided, all metals below and including that int level are considerd + by default this function returns the maximum metal seperation of metals1-5 + """ + if type(metal_levels)==int: + metal_levels = range(1,metal_levels+1) + metal_levels = metal_levels if isinstance(metal_levels,Iterable) else [metal_levels] + if len(metal_levels)<1: + raise ValueError("metal levels cannot be empty list") + if type(metal_levels[0])==int: + metal_levels = [f"met{i}" for i in metal_levels] + sep_rules = list() + for met in metal_levels: + sep_rules.append(self.get_grule(met)["min_separation"]) + return self.snap_to_2xgrid(max(sep_rules)) + + @validate_arguments + def snap_to_2xgrid(self, dims: Union[list[Union[float,Decimal]], Union[float,Decimal]], return_type: Literal["decimal","float","same"]="float", snap4: bool=False) -> Union[list[Union[float,Decimal]], Union[float,Decimal]]: + """snap all numbers in dims to double the grid size. + This is useful when a generator accepts a size or dimension argument + because there is a chance the cell may be centered (resulting in off grid components) + args: + dims = a list OR single number specifying the dimensions to snap to grid + return_type = return a decimal, float, or the same type that was passed to the function + snap4: snap to 4xgrid (Defualt false) + """ + dims = dims if isinstance(dims, Iterable) else [dims] + dimtype_in = type(dims[0]) + dims = [Decimal(str(dim)) for dim in dims] # process in decimals + grid = 2 * Decimal(str(self.grid_size)) + grid = grid if grid else Decimal('0.001') + grid = 2*grid if snap4 else grid + # snap dims to grid + snapped_dims = list() + for dim in dims: + snapped_dim = grid * (dim / grid).quantize(1, rounding=ROUND_UP) + snapped_dims.append(snapped_dim) + # convert to correct type + if return_type=="float" or (return_type=="same" and dimtype_in==float): + snapped_dims = [float(snapped_dim) for snapped_dim in snapped_dims] + # correctly return list or single element + return snapped_dims[0] if len(snapped_dims)==1 else snapped_dims + diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/__init__.py b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/__init__.py similarity index 53% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/__init__.py rename to openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/__init__.py index ac744c839..a87c179d5 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/__init__.py +++ b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/__init__.py @@ -2,4 +2,4 @@ Usage at the package level: from pdk.sky130_mapped import sky130_mapped_pdk """ -from glayout.pdk.sky130_mapped.sky130_mapped import sky130_mapped_pdk +from glayout.flow.pdk.sky130_mapped.sky130_mapped import sky130_mapped_pdk diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/grules.py b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/grules.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/grules.py rename to openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/grules.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/sky130.lydrc b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130.lydrc similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/sky130.lydrc rename to openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130.lydrc diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/sky130_add_npc.py b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_add_npc.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/sky130_add_npc.py rename to openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_add_npc.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/sky130_mapped.py b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_mapped.py similarity index 57% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/sky130_mapped.py rename to openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_mapped.py index b0d21547c..b07f09339 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/pdk/sky130_mapped/sky130_mapped.py +++ b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_mapped.py @@ -2,12 +2,11 @@ usage: from sky130_mapped import sky130_mapped_pdk """ -from ..mappedpdk import MappedPDK +from ..mappedpdk import MappedPDK, SetupPDKFiles from ..sky130_mapped.grules import grulesobj from pathlib import Path from ..sky130_mapped.sky130_add_npc import sky130_add_npc - # use mimcap over metal 3 sky130_glayer_mapping = { "capmet": (89, 44), @@ -31,8 +30,24 @@ "dnwell": (64,18), } +openfasoc_dir = Path(__file__).resolve().parent.parent.parent.parent.parent.parent.parent + +klayout_drc_file = Path(__file__).resolve().parent / "sky130.lydrc" +pdk_root = Path('/usr/bin/miniconda3/share/pdk/') +lvs_schematic_ref_file = openfasoc_dir / "common" / "platforms" / "sky130hd" / "cdl" / "sky130_fd_sc_hd.spice" +magic_drc_file = pdk_root / "sky130A" / "libs.tech" / "magic" / "sky130A.magicrc" +lvs_setup_tcl_file = pdk_root / "sky130A" / "libs.tech" / "netgen" / "sky130A_setup.tcl" +temp_dir = None -sky130_lydrc_file_path = Path(__file__).resolve().parent / "sky130.lydrc" +pdk_files = SetupPDKFiles( + pdk_root=pdk_root, + klayout_drc_file=klayout_drc_file, + lvs_schematic_ref_file=lvs_schematic_ref_file, + lvs_setup_tcl_file=lvs_setup_tcl_file, + magic_drc_file=magic_drc_file, + temp_dir=temp_dir, + pdk='sky130' +).return_dict_of_files() sky130_mapped_pdk = MappedPDK( @@ -44,7 +59,7 @@ 'mimcap': 'sky130_fd_pr__cap_mim_m3_1' }, grules=grulesobj, - klayout_lydrc_file=sky130_lydrc_file_path, + pdk_files=pdk_files, default_decorator=sky130_add_npc ) # set the grid size diff --git a/openfasoc/generators/gdsfactory-gen/glayout/placement/__init__.py b/openfasoc/generators/glayout/glayout/flow/pdk/util/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/placement/__init__.py rename to openfasoc/generators/glayout/glayout/flow/pdk/util/__init__.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/comp_utils.py b/openfasoc/generators/glayout/glayout/flow/pdk/util/comp_utils.py similarity index 88% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/util/comp_utils.py rename to openfasoc/generators/glayout/glayout/flow/pdk/util/comp_utils.py index f47c2be3c..96f01dd51 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/comp_utils.py +++ b/openfasoc/generators/glayout/glayout/flow/pdk/util/comp_utils.py @@ -7,9 +7,9 @@ from decimal import Decimal from gdsfactory.functions import transformed from gdsfactory.functions import move as __gf_move -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from gdstk import rectangle as primitive_rectangle -from .port_utils import add_ports_perimeter, rename_ports_by_list +from .port_utils import add_ports_perimeter, rename_ports_by_list, parse_direction @validate_arguments @@ -23,6 +23,32 @@ def evaluate_bbox(custom_comp: Union[Component, ComponentReference], return_deci return (float(width),float(height)) +@validate_arguments +def center_to_edge_distance(custom_comp: Union[Component, ComponentReference], direction: Union[str,int]) -> float: + """specifies the distance between the center of custom_comp and a specified edge given by the direction argument + the component is considerd a rectangle (using the bounding box), such there are 4 edges + Args: + custom_comp (Component | ComponentReference): Component we want the center of + direction (str | int): the edge we are interested in + Returns: + float: absolute distance between custom_comp center and N,S,E, or W edge + """ + compbbox = custom_comp.bbox + #center = prec_center(custom_comp) + center = custom_comp.center + direction = parse_direction(direction) + if direction==1:# West edge + distance = center[0] - compbbox[0][0] + elif direction==2:# North edge + distance = center[1] - compbbox[1][1] + elif direction==3:# East edge + distance = center[0] - compbbox[1][0] + elif direction==4:# South edge + distance = center[1] - compbbox[0][1] + else: + raise ValueError("unknown error with direction in function center_to_edge_distance (comp_utils)") + return snap_to_grid(abs(distance),2) + @validate_arguments def move(custom_comp: Union[Port, ComponentReference, Component], offsetxy: tuple[float,float] = (0,0), destination: Optional[tuple[Optional[float],Optional[float]]]=None, layer: Optional[tuple[int,int]]=None) -> Union[Port, ComponentReference, Component]: """moves custom_comp @@ -134,19 +160,19 @@ def align_comp_to_port( else: raise ValueError("port must be vertical or horizontal") else: - xalign = (alignment[0] or "none").lower() - yalign = (alignment[1] or "none").lower() + xalign = (alignment[0] or "none").lower().strip() + yalign = (alignment[1] or "none").lower().strip() # compute translation x amount for x alignment x_movcenter = align_to.center[0] - ccenter[0] x_mov_lr = abs(xdim/2 if is_EW else (width-xdim)/2) if "none" in xalign: xmov = 0 - elif "l" in xalign: + elif "l" in xalign[0]: xmov = x_movcenter - x_mov_lr - elif "r" in xalign: - xmov = x_movcenter + x_mov_lr - elif "c" in xalign: + elif "c" in xalign[0]: xmov = x_movcenter + elif "r" in xalign[0]: + xmov = x_movcenter + x_mov_lr else: raise ValueError("please specify valid x alignment of l/r/c/None") # compute translation y amount for y alignment @@ -154,11 +180,11 @@ def align_comp_to_port( y_move_updown = abs((width-ydim)/2 if is_EW else ydim/2) if "none" in yalign: ymov = 0 - elif "t" in yalign: - ymov = y_movcenter + y_move_updown - elif "b" in yalign: + elif "b" in yalign[0]: ymov = y_movcenter - y_move_updown - elif "c" in yalign: + elif "t" in yalign[0]: + ymov = y_movcenter + y_move_updown + elif "c" in yalign[0]: ymov = y_movcenter else: raise ValueError("please specify valid y alignment of t/b/c/None") @@ -246,7 +272,7 @@ def prec_center(custom_comp: Union[Component,ComponentReference], return_decimal return to_float(correctionxy) @validate_arguments -def prec_ref_center(custom_comp: Union[Component,ComponentReference], destination: Optional[tuple[float,float]]=None) -> ComponentReference: +def prec_ref_center(custom_comp: Union[Component,ComponentReference], destination: Optional[tuple[float,float]]=None, snapmov2grid: bool=False) -> ComponentReference: """instead of using component.ref_center() to get a ref to center at origin, use this function which will return a centered ref you can then run component.add(prec_ref_center(custom_comp)) to add the reference to your component @@ -258,7 +284,10 @@ def prec_ref_center(custom_comp: Union[Component,ComponentReference], destinatio if destination is not None: xcor += destination[0] ycor += destination[1] - return compref.movex(xcor).movey(ycor) + if snapmov2grid: + compref.movex(snap_to_grid(xcor,2)).movey(snap_to_grid(ycor,2)) + else: + return compref.movex(xcor).movey(ycor) diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/component_array_create.py b/openfasoc/generators/glayout/glayout/flow/pdk/util/component_array_create.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/util/component_array_create.py rename to openfasoc/generators/glayout/glayout/flow/pdk/util/component_array_create.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/port_utils.py b/openfasoc/generators/glayout/glayout/flow/pdk/util/port_utils.py similarity index 70% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/util/port_utils.py rename to openfasoc/generators/glayout/glayout/flow/pdk/util/port_utils.py index 85ec65fed..cbf4b7414 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/port_utils.py +++ b/openfasoc/generators/glayout/glayout/flow/pdk/util/port_utils.py @@ -7,10 +7,104 @@ from pathlib import Path import pickle from PrettyPrint import PrettyPrintTree +import math @validate_arguments -def rename_component_ports(custom_comp: Component, rename_function: Callable[[str, Port], str]) -> Component: +def parse_direction(direction: Union[int, str]) -> int: + """returns 1,2,3,4 (W,N,E,S) + + Args: + direction (int | str): a direction description either string or int + ****direction descrption could be north/south/east/west or left/right/up/down, etc. + + Returns: + int: 1=W, 2=N, 3=E, 4=S + """ + if isinstance(direction, int): + if direction<1 or direction>4: + raise ValueError(f"direction was specified as int {direction}, but int directions are 1,2,3, or 4") + else: + return direction + else:# direction is a string + cmp = direction.strip().lower()[0] + if cmp=="l" or cmp=="w" or cmp=="1": + return 1 + elif cmp=="u" or cmp=="n" or cmp=="2": + return 2 + elif cmp=="r" or cmp=="e" or cmp=="3": + return 3 + elif cmp=="d" or cmp=="s" or cmp=="4": + return 4 + else: + raise ValueError(f"failed to parse direction string {direction}") + + +def proc_angle(angle: float) -> int: + """round an angle in degrees to nearest int and converts to an angle [-180,180] + + Args: + angle (float): angle in degrees + + Returns: + float: angle in degrees [-180,180] + """ + angle = round(angle) + angle = angle % 360 + if angle > 180: + angle -= 360 + return angle + + +@validate_arguments +def ports_parallel(edge1: Port, edge2: Port) -> bool: + """returns True if the provided ports are parralel (same or 180degree opposite directions) + Requires ports are manhattan + Args: + edge1 (Port) + edge2 (Port) + Returns: + bool: True if the provided ports are parralel + """ + assert_port_manhattan([edge1,edge2]) + e1orientation = abs(proc_angle(edge1.orientation)) + e2orientation = abs(proc_angle(edge2.orientation)) + if e1orientation==e2orientation: + return True + if (e1orientation==180 and e2orientation==0) or (e1orientation==0 and e2orientation==180): + return True + return False + + +@validate_arguments +def ports_inline(edge1: Port, edge2: Port, abstolerance: float=0.1) -> bool: + """Check if two ports are inline within a tolerance. + + Args: + edge1 (Port): The first port. + edge2 (Port): The second port. + abstolerance (float, optional): The absolute tolerance for inline check.. Defaults to 0.1. + + Returns: + bool: True if the ports are inline within the given tolerance. + """ + # trivial cases and error checking + assert_port_manhattan([edge1, edge2]) + if not(ports_parallel(edge1, edge2)): + return False + # given ports are parallel, determine coordinates of value + e1orientation = abs(proc_angle(edge1.orientation)) + if e1orientation == 90: + centers = sorted([edge1.center[0], edge2.center[0]]) + else: + centers = sorted([edge1.center[1], edge2.center[1]]) + # determine if ports are inline within tolerance bounds + return not(centers[0] < (centers[1] - abstolerance)) + + + +@validate_arguments +def rename_component_ports(custom_comp: Union[Component, ComponentReference], rename_function: Callable[[str, Port], str]) -> Union[Component, ComponentReference]: """uses rename_function(str, Port) -> str to decide which ports to rename. rename_function accepts the current port name (string) and current port (Port) then returns the new port name rename_function can return new name = current port name, in which case the name will not change @@ -24,9 +118,7 @@ def rename_component_ports(custom_comp: Component, rename_function: Callable[[st # error checking if not pname == pobj.name: raise ValueError("component may have an invalid ports dict") - new_name = rename_function(pname, pobj) - names_to_modify.append((pname,new_name)) # modify names for namepair in names_to_modify: @@ -67,7 +159,7 @@ def rename_ports_by_orientation__call(old_name: str, pobj: Port) -> str: return new_name @validate_arguments -def rename_ports_by_orientation(custom_comp: Component) -> Component: +def rename_ports_by_orientation(custom_comp: Union[Component, ComponentReference]) -> Union[Component, ComponentReference]: """replaces the last part of the port name (after the last underscore, unless name is e1/2/3/4) with a direction direction is one of N,E,S,W @@ -103,6 +195,19 @@ def rename_ports_by_list(custom_comp: Component, replace_list: list[tuple[str,st return rename_component_ports(custom_comp, rename_func) +def remove_ports_with_prefix(custom_comp: Component, prefix: str) -> Component: + """remove all ports in custom_comp which begin with prefix""" + # determine which ports to remove + remove_list = list() + for prt in custom_comp.ports.keys(): + if prt.startswith(prefix): + remove_list.append(prt) + # remove the ports + for prt in remove_list: + custom_comp.ports.pop(prt) + return custom_comp + + @validate_arguments def add_ports_perimeter(custom_comp: Component, layer: tuple[int, int], prefix: Optional[str] = "_") -> Component: """adds ports to the outside perimeter of a cell @@ -231,8 +336,32 @@ def print_ports(custom_comp: Union[Component, ComponentReference], names_only: O print() +def create_private_ports(custom_comp: Union[Component, ComponentReference], port_paths: Optional[Union[str,list[str]]] = None) -> list[Port]: + """returns a list with a copy ports for children of the port_paths specified + the ports have _private appended + Args: + custom_comp (Component): comp to consider PortTree for + port_paths (str, list[str], optional): path along the PortTree to consider. Defaults to None (all ports are copied) + ****NOTE: if a list is provided, then it will consider all paths in the list + Returns: + list[Port]: copies of all ports along port_path, these ports end with _private + """ + # arg setup + bypass = port_paths is None + if bypass: + port_paths = [] + else: + if isinstance(port_paths, str): + port_paths = [port_paths] + # find all matching ports + ports_to_add = list() + for port in custom_comp.get_ports_list(): + if any([port.name.startswith(port_path) for port_path in port_paths]) or bypass: + ports_to_add.append(port.copy(name=port.name+"_private")) + return ports_to_add + class PortTree: - """PortTree helps a glayout programmer visualize the ports in a component + """PortTree helps a glayout.flow.programmer visualize the ports in a component \"_\" should represent a level of hiearchy (much like a directory). think of this like psuedo directories Initialize a PortTree from a Component or ComponentReference then use self.ls to list all ports/subdirectories in a directory @@ -303,12 +432,30 @@ def get_val(self, node: tuple[str, dict]) -> str: """returns value of a node, (node might be a PortTree)""" return node[0] if isinstance(node, tuple) else self.name + def get_node(self, port_path: Optional[str] = None) -> tuple[str, dict]: + """get a node name and children from a port_path + Args: + port_path (str, optional): psuedo path to a node in this PortTree. Defaults to None (returns root of the tree) + Returns: + list[tuple[str, dict]]: name and children of the specified node + """ + if port_path is None: + return self.name, self.tree + else: + current_children = self.tree + current_name = self.name + for node in port_path.split("_"): + current_children = current_children[node] + current_name = node + return current_name, current_children + + def print(self, savetofile: bool=True, default_opts: bool=True, depth: Optional[int]=None, outfile_name: Optional[str]=None, **kwargs): """prints output to terminal directly using prettyprinttree pypi package args: depth = max depth to print. this is a kwarg but since it so common, it should be specfied from depth arg savetofile = saves print output to a txt file rather than printing to terminal (easier to view, but without nice formatting) - default_opts = bool=True results in using glayout recommended default print arguments + default_opts = bool=True results in using glayout.flow.recommended default print arguments kwargs -> kwargs are prettyprint options passed directly to prettyprint. ****NOTE: kwargs override all other options """ @@ -328,20 +475,20 @@ def print(self, savetofile: bool=True, default_opts: bool=True, depth: Optional[ def print_port_tree_all_cells() -> list: - """print the PortTree for most of the glayout cells and save as a text file. + """print the PortTree for most of the glayout.flow.cells and save as a text file. returns a list of components """ - from glayout.primitives.via_gen import via_stack, via_array - from glayout.opamp import opamp - from glayout.primitives.mimcap import mimcap - from glayout.primitives.mimcap import mimcap_array - from glayout.primitives.guardring import tapring - from glayout.primitives.fet import multiplier, nmos, pmos - from glayout.diff_pair import diff_pair - 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.pdk.sky130_mapped import sky130_mapped_pdk as pdk + from glayout.flow.primitives.via_gen import via_stack, via_array + from glayout.flow.opamp import opamp + from glayout.flow.primitives.mimcap import mimcap + from glayout.flow.primitives.mimcap import mimcap_array + from glayout.flow.primitives.guardring import tapring + from glayout.flow.primitives.fet import multiplier, nmos, pmos + from glayout.flow.diff_pair import diff_pair + 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.pdk.sky130_mapped import sky130_mapped_pdk as pdk from gdsfactory.port import Port print("saving via_stack, via_array, opamp, mimcap, mimcap_array, tapring, multiplier, nmos, pmos, diff_pair, straight_route, c_route, L_route Ports to txt files") celllist = list() @@ -362,7 +509,7 @@ def print_port_tree_all_cells() -> list: celllist.append(["c_route",c_route(pdk, psuedo_porta, psuedo_porta,extension=2)]) celllist.append(["opamp",opamp(pdk)]) for name, py_cell in celllist: - from glayout import __version__ as glayoutvinfo - glayoutv = str(glayoutvinfo) - PortTree(py_cell,name=name).print(depth=5,outfile_name=name+"_v"+glayoutv+"_tree.txt",default_orientation=True) + from glayout import __version__ as glayoutinfo + vglayout = str(glayoutinfo) + PortTree(py_cell,name=name).print(depth=5,outfile_name=name+"_v"+vglayout+"_tree.txt",default_orientation=True) return celllist diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/print_rules.py b/openfasoc/generators/glayout/glayout/flow/pdk/util/print_rules.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/util/print_rules.py rename to openfasoc/generators/glayout/glayout/flow/pdk/util/print_rules.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/pdk/util/snap_to_grid.py b/openfasoc/generators/glayout/glayout/flow/pdk/util/snap_to_grid.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/pdk/util/snap_to_grid.py rename to openfasoc/generators/glayout/glayout/flow/pdk/util/snap_to_grid.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/primitives/__init__.py b/openfasoc/generators/glayout/glayout/flow/placement/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/primitives/__init__.py rename to openfasoc/generators/glayout/glayout/flow/placement/__init__.py diff --git a/openfasoc/generators/glayout/glayout/flow/placement/common_centroid_ab_ba.py b/openfasoc/generators/glayout/glayout/flow/placement/common_centroid_ab_ba.py new file mode 100644 index 000000000..760094de1 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/placement/common_centroid_ab_ba.py @@ -0,0 +1,222 @@ +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.routing.L_route import L_route +from glayout.flow.routing.c_route import c_route +from glayout.flow.primitives.fet import nmos, pmos +from glayout.flow.primitives.via_gen import via_stack, via_array +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, movex, move, movey, align_comp_to_port, prec_ref_center, center_to_edge_distance +from glayout.flow.pdk.util.port_utils import set_port_orientation, rename_ports_by_orientation, create_private_ports +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.primitives.guardring import tapring +from gdsfactory.components import rectangle +from typing import Union, Optional +from itertools import product + +from gdsfactory import Component + + +def common_centroid_ab_ba( + pdk: MappedPDK, + width: float = 3, + fingers: int = 4, + length: Optional[float] = None, + n_or_p_fet: bool = True, + rmult: int = 1, + dummy: Union[bool, tuple[bool, bool]] = True, + substrate_tap: bool=True +) -> Component: + """create a comcentroid 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 comcentroid is made of nfets else it is made of pfets + substrate_tap: if true place a tapring around the comcentroid (connects on met1) + """ + # TODO: error checking + pdk.activate() + comcentroid = 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) + well, sdglayer = "pwell", "n+s/d" + 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) + well, sdglayer = "nwell", "p+s/d" + fetRdims = evaluate_bbox(fetR.flatten().remove_layers(layers=[pdk.get_glayer(well)])) + fetLdims = evaluate_bbox(fetL.flatten().remove_layers(layers=[pdk.get_glayer(well)])) + # place and flip top transistors such that the drains of bottom and top point towards eachother + a_topl = comcentroid << fetL + a_topl = rename_ports_by_orientation(a_topl.mirror_y()) + b_topr = comcentroid << fetR + b_topr = rename_ports_by_orientation(b_topr.mirror_y()) + a_botr = comcentroid << fetR + b_botl = comcentroid << fetL + prec_ref_center(a_topl, snapmov2grid=True) + prec_ref_center(b_topr, snapmov2grid=True) + prec_ref_center(a_botr, snapmov2grid=True) + prec_ref_center(b_botl, snapmov2grid=True) + # setup for routing (need viadims to know how far to seperate transistors) + glayer1 = pdk.layer_to_glayer(a_topl.ports["multiplier_0_drain_E"].layer) + glayer2 = glayer1[0:-1] + str(int(glayer1[-1])+1) + glayer0 = glayer1[0:-1] + str(int(glayer1[-1])-1) + g1g2via = via_stack(pdk,glayer1,glayer2) + g0g1via = via_stack(pdk,glayer0,glayer1) + # move transistors into position + min_spacing_y = pdk.snap_to_2xgrid(1*(g1g2via.ysize - pdk.get_grule(glayer1)["min_width"])+pdk.get_grule(glayer1)["min_separation"]) + extra_g1g2_spacing = pdk.snap_to_2xgrid(max(pdk.get_grule(glayer2)["min_separation"]-pdk.get_grule(glayer1)["min_separation"],0)) + min_spacing_y += extra_g1g2_spacing + min_spacing_x = 3*pdk.get_grule(glayer1)["min_separation"] + 2*g0g1via.xsize - 2*pdk.get_grule("active_diff",sdglayer)["min_enclosure"] + min_spacing_x = pdk.snap_to_2xgrid(max(min_spacing_x, pdk.get_grule(sdglayer)["min_separation"])) + a_topl.movex(0-fetLdims[0]/2-min_spacing_x/2).movey(pdk.snap_to_2xgrid(fetRdims[1]/2+min_spacing_y/2)) + b_topr.movex(fetLdims[0]/2+min_spacing_x/2).movey(pdk.snap_to_2xgrid(fetLdims[1]/2+min_spacing_y/2)) + a_botr.movex(fetLdims[0]/2+min_spacing_x/2).movey(pdk.snap_to_2xgrid(-fetLdims[1]/2-min_spacing_y/2)) + b_botl.movex(0-fetLdims[0]/2-min_spacing_x/2).movey(pdk.snap_to_2xgrid(-fetLdims[1]/2-min_spacing_y/2)) + comcentroid.add_padding(default=0,layers=[pdk.get_glayer(well)]) + # if substrate tap place substrate tap, and route dummy to substrate tap + if substrate_tap: + tapref = comcentroid << tapring(pdk,evaluate_bbox(comcentroid,padding=1))#,horizontal_glayer="met1") + comcentroid.add_ports(tapref.get_ports_list(),prefix="tap_") + try: + comcentroid<bdrain) + comcentroid << straight_route(pdk, vdrainb1.ports["top_met_W"],comcentroid.ports["br_multiplier_0_leftsd_top_met_E"],glayer2=glayer1) + vdrainb2 = comcentroid << g0g1via # second via + align_comp_to_port(vdrainb2, comcentroid.ports["br_multiplier_0_drain_W"],alignment=("left","c")) + vdrainb2.movex(-pdk.get_grule(glayer1)["min_separation"]) + vdrainb2_mdprt = movex(vdrainb2.ports["bottom_met_N"],-pdk.get_grule("met2","via1")["min_enclosure"]) + vdrainb2_mdprt.width = vdrainb2_mdprt.width + 2*pdk.get_grule("met2","via1")["min_enclosure"] + comcentroid << straight_route(pdk, vdrainb2_mdprt, vdrainb1.ports["bottom_met_S"]) + comcentroid << L_route(pdk, vdrainb2.ports["top_met_N"],comcentroid.ports["tl_multiplier_0_source_E"]) + # agate to agate + gate2rt_sep = pdk.get_grule(glayer2)["min_separation"] + vgatea1 = comcentroid << g1g2via# first via + align_comp_to_port(vgatea1,comcentroid.ports["tl_multiplier_0_gate_E"],alignment=("right","bottom")) + vgatea2 = comcentroid << g1g2via# second via + align_comp_to_port(vgatea2,comcentroid.ports["br_multiplier_0_gate_S"],alignment=("right","bottom")) + vgatea2.movey(-gate2rt_sep) + comcentroid << straight_route(pdk, vgatea2.ports["bottom_met_S"], comcentroid.ports["br_multiplier_0_gate_N"]) + g1extension = pdk.util_max_metal_seperation()+pdk.snap_to_2xgrid(comcentroid.ports["tr_multiplier_0_plusdoped_E"].center[0] - vgatea2.ports["top_met_E"].center[0]) + cext1 = comcentroid << c_route(pdk, vgatea2.ports["top_met_E"], vgatea1.ports["top_met_E"], cglayer=glayer2, extension=g1extension) + comcentroid.add_ports(ports=cext1.get_ports_list(),prefix="A_gate_route_") + # bgate to bgate + vgateb1 = comcentroid << g1g2via# first via + align_comp_to_port(vgateb1,comcentroid.ports["bl_multiplier_0_gate_E"],alignment=("right","top")) + vgateb2 = comcentroid << g1g2via# second via + align_comp_to_port(vgateb2,comcentroid.ports["tr_multiplier_0_gate_S"],alignment=("right","top")) + vgateb2.movey(gate2rt_sep) + comcentroid << straight_route(pdk, vgateb2.ports["bottom_met_N"], comcentroid.ports["tr_multiplier_0_gate_S"]) + g2extension = pdk.util_max_metal_seperation()+pdk.snap_to_2xgrid(abs(comcentroid.ports["tl_multiplier_0_plusdoped_W"].center[0] - vgateb1.ports["top_met_W"].center[0])) + cext2 = comcentroid << c_route(pdk, vgateb2.ports["top_met_W"], vgateb1.ports["top_met_W"], cglayer=glayer2, extension=g2extension) + comcentroid.add_ports(ports=cext2.get_ports_list(),prefix="B_gate_route_") + # create better toplevel ports + b_drainENS = comcentroid << straight_route(pdk, comcentroid.ports["tr_multiplier_0_drain_E"], movex(cext1.ports["con_N"],cext2.ports["con_N"].width/2+pdk.util_max_metal_seperation()), glayer2=glayer1) + a_drainENS = comcentroid << straight_route(pdk, comcentroid.ports["br_multiplier_0_drain_E"], movex(cext1.ports["con_N"],cext2.ports["con_N"].width/2+pdk.util_max_metal_seperation()), glayer2=glayer1) + b_sourceENS = comcentroid << straight_route(pdk, comcentroid.ports["tr_multiplier_0_source_E"], movex(cext1.ports["con_N"],cext2.ports["con_N"].width/2+pdk.util_max_metal_seperation()), glayer2=glayer1) + a_sourceENS = comcentroid << straight_route(pdk, comcentroid.ports["br_multiplier_0_source_E"], movex(cext1.ports["con_N"],cext2.ports["con_N"].width/2+pdk.util_max_metal_seperation()), glayer2=glayer1) + b_drainW = comcentroid << straight_route(pdk, comcentroid.ports["bl_multiplier_0_drain_W"], movex(cext2.ports["con_N"],-cext2.ports["con_N"].width/2-pdk.util_max_metal_seperation()), glayer2=glayer1) + a_drainW = comcentroid << straight_route(pdk, comcentroid.ports["tl_multiplier_0_drain_W"], movex(cext2.ports["con_N"],-cext2.ports["con_N"].width/2-pdk.util_max_metal_seperation()), glayer2=glayer1) + b_sourceW = comcentroid << straight_route(pdk, comcentroid.ports["bl_multiplier_0_source_W"], movex(cext2.ports["con_N"],-cext2.ports["con_N"].width/2-pdk.util_max_metal_seperation()), glayer2=glayer1) + a_sourceW = comcentroid << straight_route(pdk, comcentroid.ports["tl_multiplier_0_source_W"], movex(cext2.ports["con_N"],-cext2.ports["con_N"].width/2-pdk.util_max_metal_seperation()), glayer2=glayer1) + # add the ports + def makeNorS(portin, direction: str): + mdprt = set_port_orientation(movex(portin.copy(),(-1 if portin.name.endswith("E") else 1)*pdk.snap_to_2xgrid(portin.width/2)),direction) + mdprt.name = (mdprt.name.strip("EW") + direction.strip().capitalize()).removeprefix("route_") + return movey(mdprt,(1 if direction.endswith("N") else -1)*pdk.snap_to_2xgrid(mdprt.width/2)) + def addENS(topcomp: Component, straightrouteref, device: str, pin: str): + # device is A or B and pin is source drain or gate + eastport = straightrouteref.ports["route_E"].copy() + eastport.name = eastport.name.removeprefix("route_") + topcomp.add_ports(ports=[eastport,makeNorS(eastport,"N"),makeNorS(eastport,"S")],prefix=device+"_"+pin+"_") + addENS(comcentroid,b_drainENS,"B","drain") + addENS(comcentroid,a_drainENS,"A","drain") + addENS(comcentroid,b_sourceENS,"B","source") + addENS(comcentroid,a_sourceENS,"A","source") + def localportrename(portin): + portin = portin.copy() + portin.name = portin.name.removeprefix("route_") + return portin + comcentroid.add_ports(ports=[localportrename(b_drainW.ports["route_W"])],prefix="B_drain_") + comcentroid.add_ports(ports=[localportrename(a_drainW.ports["route_W"])],prefix="A_drain_") + comcentroid.add_ports(ports=[localportrename(b_sourceW.ports["route_W"])],prefix="B_source_") + comcentroid.add_ports(ports=[localportrename(a_sourceW.ports["route_W"])],prefix="A_source_") + # better gate routes + a_gateE = comcentroid << straight_route(pdk, comcentroid.ports["br_multiplier_0_gate_E"], comcentroid.ports["A_drain_E"]) + b_gateE = comcentroid << straight_route(pdk, comcentroid.ports["tr_multiplier_0_gate_E"], comcentroid.ports["A_drain_E"]) + a_gateW = comcentroid << straight_route(pdk, comcentroid.ports["tl_multiplier_0_gate_W"], comcentroid.ports["A_drain_W"]) + b_gateW = comcentroid << straight_route(pdk, comcentroid.ports["bl_multiplier_0_gate_W"], comcentroid.ports["A_drain_W"]) + comcentroid.add_ports(ports=[localportrename(a_gateE.ports["route_E"])],prefix="A_gate_") + comcentroid.add_ports(ports=[localportrename(b_gateE.ports["route_E"])],prefix="B_gate_") + comcentroid.add_ports(ports=[localportrename(a_gateW.ports["route_W"])],prefix="A_gate_") + comcentroid.add_ports(ports=[localportrename(b_gateW.ports["route_W"])],prefix="B_gate_") + rename_north_portb = vgateb2.ports["top_met_N"].copy()# add B_gate_N + rename_north_portb.name = "B_gate_N" + comcentroid.add_ports(ports=[rename_north_portb]) + rename_south_porta = vgatea2.ports["top_met_S"].copy()# add A_gate_S + rename_south_porta.name = "A_gate_S" + comcentroid.add_ports(ports=[rename_south_porta]) + rename_south_portb = vgateb1.ports["top_met_S"].copy()# add B_gate_S + rename_south_portb.name = "B_gate_S" + comcentroid.add_ports(ports=[rename_south_portb]) + # rename ports and add private ports for smart route + comcentroid = rename_ports_by_orientation(comcentroid) + comcentroid.add_ports(create_private_ports(comcentroid,["".join(prtp) for prtp in product(["A_","B_"],["drain","source","gate"])])) + comcentroid.info["route_genid"]="common_centroid_ab_ba" + return comcentroid + diff --git a/openfasoc/generators/glayout/glayout/flow/placement/four_transistor_interdigitized.py b/openfasoc/generators/glayout/glayout/flow/placement/four_transistor_interdigitized.py new file mode 100644 index 000000000..31ac62858 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/placement/four_transistor_interdigitized.py @@ -0,0 +1,47 @@ +# 4 transistor placed in two rows (each row is an interdigitized pair of transistors) +# the 4 transistors are labeled top or bottom and transistor A or B +# top_A_, bottom_A, top_B_, bottom_B_ + +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized +from typing import Literal, Optional +from gdsfactory import Component +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, movey +from glayout.flow.primitives.guardring import tapring + +def generic_4T_interdigitzed( + pdk: MappedPDK, + top_row_device: Literal["nfet", "pfet"], + bottom_row_device: Literal["nfet", "pfet"], + numcols: int, + with_substrate_tap: bool = True, + top_kwargs: Optional[dict]=None, + bottom_kwargs: Optional[dict]=None +): + if top_kwargs is None: + top_kwargs = dict() + if bottom_kwargs is None: + bottom_kwargs = dict() + # place + toplvl = Component() + if top_row_device=="nfet": + toprow = toplvl << two_nfet_interdigitized(pdk,numcols,with_substrate_tap=False,**top_kwargs) + else: + toprow = toplvl << two_pfet_interdigitized(pdk,numcols,with_substrate_tap=False,**top_kwargs) + if bottom_row_device=="nfet": + bottomrow = toplvl << two_nfet_interdigitized(pdk,numcols,with_substrate_tap=False,**bottom_kwargs) + else: + bottomrow = toplvl << two_pfet_interdigitized(pdk,numcols,with_substrate_tap=False,**bottom_kwargs) + # move + toprow.movey(pdk.snap_to_2xgrid((evaluate_bbox(bottomrow)[1]/2 + evaluate_bbox(toprow)[1]/2 + pdk.util_max_metal_seperation()))) + # add substrate tap + if with_substrate_tap: + substrate_tap = tapring(pdk, enclosed_rectangle=pdk.snap_to_2xgrid(evaluate_bbox(toplvl.flatten(),padding=pdk.util_max_metal_seperation()))) + substrate_tap_ref = toplvl << movey(substrate_tap,destination=pdk.snap_to_2xgrid(toplvl.flatten().center[1],snap4=True)) + # add ports + toplvl.add_ports(substrate_tap_ref.get_ports_list(),prefix="substratetap_") + toplvl.add_ports(toprow.get_ports_list(),prefix="top_") + toplvl.add_ports(bottomrow.get_ports_list(),prefix="bottom_") + # flag for smart route + toplvl.info["route_genid"] = "four_transistor_interdigitized" + return toplvl diff --git a/openfasoc/generators/gdsfactory-gen/glayout/placement/two_transistor_interdigitized.py b/openfasoc/generators/glayout/glayout/flow/placement/two_transistor_interdigitized.py similarity index 55% rename from openfasoc/generators/gdsfactory-gen/glayout/placement/two_transistor_interdigitized.py rename to openfasoc/generators/glayout/glayout/flow/placement/two_transistor_interdigitized.py index bf10f1a7a..99e147789 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/placement/two_transistor_interdigitized.py +++ b/openfasoc/generators/glayout/glayout/flow/placement/two_transistor_interdigitized.py @@ -1,22 +1,22 @@ -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from pydantic import validate_arguments from gdsfactory.component import Component -from glayout.primitives.fet import nmos, pmos, multiplier -from glayout.pdk.util.comp_utils import evaluate_bbox +from glayout.flow.primitives.fet import nmos, pmos, multiplier +from glayout.flow.pdk.util.comp_utils import evaluate_bbox from typing import Literal, Union -from glayout.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list -from glayout.pdk.util.comp_utils import prec_ref_center -from glayout.routing.straight_route import straight_route +from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, create_private_ports +from glayout.flow.pdk.util.comp_utils import prec_ref_center +from glayout.flow.routing.straight_route import straight_route from gdsfactory.functions import transformed -from glayout.primitives.guardring import tapring -from glayout.pdk.util.port_utils import add_ports_perimeter +from glayout.flow.primitives.guardring import tapring +from glayout.flow.pdk.util.port_utils import add_ports_perimeter from gdsfactory.cell import clear_cache +from typing import Literal - -#from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized; from glayout.pdk.sky130_mapped import sky130_mapped_pdk as pdk; biasParams=[6,2,4]; rmult=2 +#from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized; from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as pdk; biasParams=[6,2,4]; rmult=2 @validate_arguments -def two_transistor_interdigitized( +def macro_two_transistor_interdigitized( pdk: MappedPDK, numcols: int, deviceA_and_B: Literal["nfet", "pfet"], @@ -32,7 +32,7 @@ def two_transistor_interdigitized( deviceA_and_B = the device to place for both transistors (either nfet or pfet) dummy = place dummy at the edges of the interdigitized place (true by default). you can specify tuple to place only on one side kwargs = key word arguments for device. - ****NOTE: These are the same as glayout.primitives.fet.multiplier arguments EXCLUDING dummy, sd_route_extension, and pdk options + ****NOTE: These are the same as glayout.flow.primitives.fet.multiplier arguments EXCLUDING dummy, sd_route_extension, and pdk options """ if isinstance(dummy, bool): dummy = (dummy, dummy) @@ -71,7 +71,14 @@ def two_transistor_interdigitized( devletter = "B" if i%2 else "A" prefix=devletter+"_"+str(int(i/2))+"_" idplace.add_ports(refs[-1].get_ports_list(), prefix=prefix) - #issue somehwere before line 89 + # extend poly layer for equal parasitics + for i in range(2*numcols): + desired_end_layer = pdk.layer_to_glayer(refs[i].ports["row0_col0_rightsd_top_met_N"].layer) + idplace << straight_route(pdk, refs[i].ports["row0_col0_rightsd_top_met_N"],refs[-1].ports["drain_E"],glayer2=desired_end_layer) + idplace << straight_route(pdk, refs[i].ports["leftsd_top_met_N"],refs[-1].ports["drain_E"],glayer2=desired_end_layer) + if not i%2: + desired_gate_end_layer = "poly" + idplace << straight_route(pdk, refs[i].ports["row0_col0_gate_S"], refs[-1].ports["gate_E"],glayer2=desired_gate_end_layer) # merge s/d layer for all transistors idplace << straight_route(pdk, refs[0].ports["plusdoped_W"],refs[-1].ports["plusdoped_E"]) # create s/d/gate connections extending over entire row @@ -87,6 +94,7 @@ def two_transistor_interdigitized( idplace.add_ports(ref.get_ports_list(),prefix=prefixes[i]) idplace = transformed(prec_ref_center(idplace)) idplace.unlock() + idplace.add_ports(create_private_ports(idplace, prefixes)) return idplace @@ -107,10 +115,10 @@ def two_nfet_interdigitized( numcols = a single col is actually one col for both nfets (so AB). 2 cols = ABAB ... so on dummy = place dummy at the edges of the interdigitized place (true by default). you can specify tuple to place only on one side kwargs = key word arguments for multiplier. - ****NOTE: These are the same as glayout.primitives.fet.multiplier arguments EXCLUDING dummy, sd_route_extension, and pdk options + ****NOTE: These are the same as glayout.flow.primitives.fet.multiplier arguments EXCLUDING dummy, sd_route_extension, and pdk options tie_layers: tuple[str,str] specifying (horizontal glayer, vertical glayer) or well tie ring. default=("met2","met1") """ - base_multiplier = two_transistor_interdigitized(pdk, numcols, "nfet", dummy, **kwargs) + base_multiplier = macro_two_transistor_interdigitized(pdk, numcols, "nfet", dummy, **kwargs) # tie if with_tie: tap_separation = max( @@ -163,4 +171,103 @@ def two_nfet_interdigitized( ) tapring_ref = base_multiplier << ringtoadd base_multiplier.add_ports(tapring_ref.get_ports_list(),prefix="substratetap_") + base_multiplier.info["route_genid"] = "two_transistor_interdigitized" return base_multiplier + + + +@validate_arguments +def two_pfet_interdigitized( + pdk: MappedPDK, + numcols: int, + dummy: Union[bool, tuple[bool, bool]] = True, + with_substrate_tap: bool = True, + with_tie: bool = True, + tie_layers: tuple[str,str]=("met2","met1"), + **kwargs +) -> Component: + """Currently only supports two of the same nfet instances. does NOT support multipliers (currently) + Place follows an ABABAB... pattern + args: + pdk = MappedPDK to use + numcols = a single col is actually one col for both nfets (so AB). 2 cols = ABAB ... so on + dummy = place dummy at the edges of the interdigitized place (true by default). you can specify tuple to place only on one side + kwargs = key word arguments for multiplier. + ****NOTE: These are the same as glayout.flow.primitives.fet.multiplier arguments EXCLUDING dummy, sd_route_extension, and pdk options + tie_layers: tuple[str,str] specifying (horizontal glayer, vertical glayer) or well tie ring. default=("met2","met1") + """ + base_multiplier = macro_two_transistor_interdigitized(pdk, numcols, "pfet", dummy, **kwargs) + # tie + if with_tie: + tap_separation = max( + pdk.util_max_metal_seperation(), + pdk.get_grule("active_diff", "active_tap")["min_separation"], + ) + tap_separation += pdk.get_grule("n+s/d", "active_tap")["min_enclosure"] + tap_encloses = ( + 2 * (tap_separation + base_multiplier.xmax), + 2 * (tap_separation + base_multiplier.ymax), + ) + tiering_ref = base_multiplier << tapring( + pdk, + enclosed_rectangle=tap_encloses, + sdlayer="n+s/d", + horizontal_glayer=tie_layers[0], + vertical_glayer=tie_layers[1], + ) + base_multiplier.add_ports(tiering_ref.get_ports_list(), prefix="welltie_") + try: + base_multiplier< Component: + if device=="nfet": + return two_nfet_interdigitized(pdk=pdk,numcols=numcols,dummy=dummy,with_substrate_tap=with_substrate_tap,with_tie=with_tie,tie_layers=tie_layers,**kwargs) + else: + return two_pfet_interdigitized(pdk=pdk,numcols=numcols,dummy=dummy,with_substrate_tap=with_substrate_tap,with_tie=with_tie,tie_layers=tie_layers,**kwargs) + + diff --git a/openfasoc/generators/gdsfactory-gen/glayout/placement/two_transistor_place.py b/openfasoc/generators/glayout/glayout/flow/placement/two_transistor_place.py similarity index 92% rename from openfasoc/generators/gdsfactory-gen/glayout/placement/two_transistor_place.py rename to openfasoc/generators/glayout/glayout/flow/placement/two_transistor_place.py index 3de11bcd8..6e7eac2c4 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/placement/two_transistor_place.py +++ b/openfasoc/generators/glayout/glayout/flow/placement/two_transistor_place.py @@ -1,9 +1,9 @@ -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from pydantic import validate_arguments from gdsfactory.component import Component from typing import Callable -from glayout.primitives.fet import nmos, pmos -from glayout.pdk.util.comp_utils import evaluate_bbox +from glayout.flow.primitives.fet import nmos, pmos +from glayout.flow.pdk.util.comp_utils import evaluate_bbox @validate_arguments def two_transistor_place(pdk: MappedPDK, pattern: str, deviceA: tuple[Callable, dict], deviceB: tuple[Callable, dict]) -> Component: diff --git a/openfasoc/generators/gdsfactory-gen/glayout/routing/__init__.py b/openfasoc/generators/glayout/glayout/flow/primitives/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/routing/__init__.py rename to openfasoc/generators/glayout/glayout/flow/primitives/__init__.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/primitives/fet.py b/openfasoc/generators/glayout/glayout/flow/primitives/fet.py similarity index 97% rename from openfasoc/generators/gdsfactory-gen/glayout/primitives/fet.py rename to openfasoc/generators/glayout/glayout/flow/primitives/fet.py index 3e9365df2..e99507546 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/primitives/fet.py +++ b/openfasoc/generators/glayout/glayout/flow/primitives/fet.py @@ -2,19 +2,19 @@ from gdsfactory.cell import cell from gdsfactory.component import Component, copy from gdsfactory.components.rectangle import rectangle -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.via_gen import via_array, via_stack -from glayout.primitives.guardring import tapring +from glayout.flow.primitives.via_gen import via_array, via_stack +from glayout.flow.primitives.guardring import tapring from pydantic import validate_arguments -from glayout.pdk.util.comp_utils import evaluate_bbox, to_float, to_decimal, prec_array, prec_center, prec_ref_center, movey, align_comp_to_port -from glayout.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports -from glayout.routing.c_route import c_route -from glayout.routing.L_route import L_route -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, to_float, to_decimal, prec_array, prec_center, prec_ref_center, movey, align_comp_to_port +from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, add_ports_perimeter, print_ports +from glayout.flow.routing.c_route import c_route +from glayout.flow.routing.L_route import L_route +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid from decimal import Decimal -from glayout.routing.straight_route import straight_route -from glayout.spice import Netlist +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.spice import Netlist @validate_arguments @@ -102,6 +102,7 @@ def fet_netlist( } ) +# drain is above source @cell def multiplier( pdk: MappedPDK, @@ -334,7 +335,7 @@ def __mult_array_macro( return component_snap_to_grid(rename_ports_by_orientation(final_arr)) -@cell +#@cell def nmos( pdk, width: float = 3, @@ -478,7 +479,7 @@ def nmos( return component -@cell +#@cell def pmos( pdk, width: float = 3, diff --git a/openfasoc/generators/gdsfactory-gen/glayout/primitives/guardring.py b/openfasoc/generators/glayout/glayout/flow/primitives/guardring.py similarity index 93% rename from openfasoc/generators/gdsfactory-gen/glayout/primitives/guardring.py rename to openfasoc/generators/glayout/glayout/flow/primitives/guardring.py index 2a25e7e2a..7fc387f9b 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/primitives/guardring.py +++ b/openfasoc/generators/glayout/glayout/flow/primitives/guardring.py @@ -1,17 +1,17 @@ -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from gdsfactory.cell import cell from gdsfactory.component import Component from gdsfactory.components.rectangle import rectangle from gdsfactory.components.rectangular_ring import rectangular_ring -from glayout.primitives.via_gen import via_array, via_stack +from glayout.flow.primitives.via_gen import via_array, via_stack from typing import Optional -from glayout.pdk.util.comp_utils import to_decimal, to_float, evaluate_bbox -from glayout.pdk.util.port_utils import print_ports -from glayout.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.routing.L_route import L_route +from glayout.flow.pdk.util.comp_utils import to_decimal, to_float, evaluate_bbox +from glayout.flow.pdk.util.port_utils import print_ports +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.routing.L_route import L_route -@cell +#@cell def tapring( pdk: MappedPDK, enclosed_rectangle=(2.0, 4.0), diff --git a/openfasoc/generators/gdsfactory-gen/glayout/primitives/mimcap.py b/openfasoc/generators/glayout/glayout/flow/primitives/mimcap.py similarity index 92% rename from openfasoc/generators/gdsfactory-gen/glayout/primitives/mimcap.py rename to openfasoc/generators/glayout/glayout/flow/primitives/mimcap.py index 17ddb571a..95c723644 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/primitives/mimcap.py +++ b/openfasoc/generators/glayout/glayout/flow/primitives/mimcap.py @@ -1,15 +1,15 @@ from gdsfactory.cell import cell from gdsfactory.component import Component from gdsfactory.components.rectangle import rectangle -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional -from glayout.primitives.via_gen import via_array -from glayout.pdk.util.comp_utils import prec_array, to_decimal, to_float -from glayout.pdk.util.port_utils import rename_ports_by_orientation, add_ports_perimeter, print_ports +from glayout.flow.primitives.via_gen import via_array +from glayout.flow.pdk.util.comp_utils import prec_array, to_decimal, to_float +from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, add_ports_perimeter, print_ports from pydantic import validate_arguments -from glayout.routing.straight_route import straight_route +from glayout.flow.routing.straight_route import straight_route from decimal import ROUND_UP, Decimal -from glayout.spice import Netlist +from glayout.flow.spice import Netlist @validate_arguments def __get_mimcap_layerconstruction_info(pdk: MappedPDK) -> tuple[str,str]: @@ -51,7 +51,7 @@ def __generate_mimcap_array_netlist(mimcap_netlist: Netlist, num_caps: int) -> N return arr_netlist -@cell +#@cell def mimcap( pdk: MappedPDK, size: tuple[float,float]=(5.0, 5.0) ) -> Component: @@ -86,7 +86,7 @@ def mimcap( return component -@cell +#@cell def mimcap_array(pdk: MappedPDK, rows: int, columns: int, size: tuple[float,float] = (5.0,5.0), rmult: Optional[int]=1) -> Component: """create mimcap array args: diff --git a/openfasoc/generators/glayout/glayout/flow/primitives/resistor.py b/openfasoc/generators/glayout/glayout/flow/primitives/resistor.py new file mode 100644 index 000000000..d30abcd7d --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/primitives/resistor.py @@ -0,0 +1,139 @@ +from glayout.flow.primitives.fet import pmos, nmos +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.routing.smart_route import smart_route +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.primitives.guardring import tapring +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, add_ports_perimeter +from gdsfactory.component import Component +from gdsfactory.cell import cell +from typing import Optional + +@cell +def resistor( + pdk: MappedPDK, + width: float = 5, + length: float = 1, + num_series: int = 1, + with_substrate_tap: bool = False, + with_tie: bool = False, + with_dnwell: bool = False, + rmult: Optional[int] = None, + multipliers: int = 1, + substrate_tap_layers: tuple[str, str] = ("met2", "met1"), + tie_layers: tuple[str, str] = ("met2", "met1"), +) -> Component: + """This cell represents a diode connected pfet which acts as a programmable resistor. The small signal resistance is modelled by (1/gm)||r_0, where gm is the fet's transconductance and r0 is the small signal output impedance. The cell can be instantiated with any choice of width and length. The number of resistors connected in series can be controlled using numseries (**note: they will be placed in a single line, so area saving has to be manually handled). The number of resistors in parallel connnection can be controlled using multipliers. + Note that parallel and series resistors can be used simultaneously, but the parallel-ness will be applied to all resistors in series. The cell can be used and routed separately should a more complex combination of resistances be required + + Args: + pdk (MappedPDK): the process design kit to be used + width (float, optional): the width of each pfet. Defaults to 5. + length (float, optional): the length of each pfet. Defaults to 1. + num_series (int, optional): the number of pfets connected in series. Defaults to 1. + with_substrate_tap (bool, optional): the presence of substrate tap. Defaults to False. + with_tie (bool, optional): the presence of tie. Defaults to False. + with_dnwell (bool, optional): the presence of dnwell. Defaults to False. + rmult (Optional[int], optional): the routing multiplier (controls routing width). Defaults to None. + multipliers (int, optional): the number of pfets connected in parallel. Defaults to 1. + substrate_tap_layers (tuple[str, str], optional): the layers in the substrate tapring. Defaults to ("met2", "met1"). + tie_layers (tuple[str, str], optional): the layers in the tie layer tapring. Defaults to ("met2", "met1"). + + Returns: + Component: an instance of the resistor cell + """ + toplvl = Component() + max_sep = pdk.util_max_metal_seperation() + if num_series == 1: + pfet_reference = toplvl << pmos(pdk, width=width, length=length, with_substrate_tap=with_substrate_tap, with_tie=with_tie, dnwell=with_dnwell, rmult=rmult, multipliers=multipliers, substrate_tap_layers=substrate_tap_layers, tie_layers=tie_layers, with_dummy=False) + toplvl.add_ports(pfet_reference.ports, prefix='pfet_') + + # short gate and drain + diode_connect = toplvl << c_route(pdk, pfet_reference.ports['multiplier_0_gate_W'], pfet_reference.ports['multiplier_0_drain_W']) + + else: + pfet_references = [] + diode_connect_references = [] + pfet_reference_0 = toplvl << pmos(pdk, width=width, length=length, with_substrate_tap=False, with_tie=False, dnwell=False, rmult=rmult, multipliers=multipliers, substrate_tap_layers=substrate_tap_layers, tie_layers=tie_layers, with_dummy=False) + diode_connect_0 = toplvl << c_route(pdk, pfet_reference_0.ports['multiplier_0_gate_W'], pfet_reference_0.ports['multiplier_0_drain_W']) + diode_connect_references.append(diode_connect_0) + + toplvl.add_ports(pfet_reference_0.ports, prefix='pfet_0_') + pfet_references.append(pfet_reference_0) + for i in range(1, num_series): + pfet_reference = (toplvl << pmos(pdk, width=width, length=length, with_substrate_tap=False, with_tie=False, dnwell=False, rmult=rmult,multipliers=multipliers, substrate_tap_layers=substrate_tap_layers, tie_layers=tie_layers, with_dummy=False)).movey(i * (evaluate_bbox(pfet_reference_0)[1] + max_sep)) + + pfet_references.append(pfet_reference) + if i < num_series - 1: + toplvl.add_ports(pfet_reference.ports, prefix=f'pfet_{i}_') + + # short gate and drain + diode_connect = toplvl << c_route(pdk, pfet_reference.ports['multiplier_0_gate_W'], pfet_reference.ports['multiplier_0_drain_W']) + diode_connect_references.append(diode_connect) + # connect drain and source of previous and present pfet + if multipliers > 1: + extension = 1 * ((i % 2) + 1) + else: + extension = 0.5 * ((i % 2)) + toplvl << c_route(pdk, pfet_references[i-1].ports['multiplier_0_source_E'], pfet_reference.ports['multiplier_0_drain_E'], extension=extension) + + # add tie if tie + if with_tie: + tap_separation = max( + pdk.get_grule("met2")["min_separation"], + pdk.get_grule("met1")["min_separation"], + pdk.get_grule("active_diff", "active_tap")["min_separation"], + ) + tap_separation += pdk.get_grule("n+s/d", "active_tap")["min_enclosure"] + tap_encloses = ( + (evaluate_bbox(toplvl)[0] + max_sep), + (evaluate_bbox(toplvl)[1] + max_sep), + ) + ringtoadd = tapring( + pdk, + enclosed_rectangle=tap_encloses, + sdlayer="n+s/d", + horizontal_glayer=tie_layers[0], + vertical_glayer=tie_layers[1], + ) + tapring_ref = (toplvl << ringtoadd).movey(((evaluate_bbox(pfet_reference_0)[1] + max_sep) * ((num_series - 1)/2) )) + toplvl.add_ports(tapring_ref.get_ports_list(),prefix="tie_") + for row in range(multipliers): + for dummyside, tieside in [("L","W"),("R","E")]: + try: + toplvl << straight_route(pdk, toplvl.ports[f"multiplier_{row}_dummy_{dummyside}_gsdcon_top_met_W"], toplvl.ports[f"tie_{tieside}_top_met_{tieside}"],glayer2="met1") + except KeyError: + pass + + # add nwell + nwell_glayer = "dnwell" if with_dnwell else "nwell" + toplvl.add_padding( + layers=(pdk.get_glayer(nwell_glayer),), + default=pdk.get_grule("active_tap", nwell_glayer)["min_enclosure"], + ) + toplvl = add_ports_perimeter(toplvl, layer=pdk.get_glayer(nwell_glayer),prefix="well_") + + # add substrate tap if needed + if with_substrate_tap: + substrate_tap_separation = pdk.get_grule("dnwell", "active_tap")[ + "min_separation" + ] + substrate_tap_encloses = ( + (evaluate_bbox(toplvl)[0] + max_sep), + (evaluate_bbox(toplvl)[1] + max_sep), + ) + ringtoadd = tapring( + pdk, + enclosed_rectangle=substrate_tap_encloses, + sdlayer="p+s/d", + horizontal_glayer=substrate_tap_layers[0], + vertical_glayer=substrate_tap_layers[1], + ) + tapring_ref = (toplvl << ringtoadd).movey(((evaluate_bbox(pfet_reference_0)[1] + max_sep) * ((num_series - 1)/2) )) + toplvl.add_ports(tapring_ref.get_ports_list(),prefix="guardring_") + + toplvl.add_ports(pfet_references[0].get_ports_list(), prefix='port1_') + toplvl.add_ports(pfet_references[-1].get_ports_list(), prefix='port2_') + + return toplvl \ No newline at end of file diff --git a/openfasoc/generators/gdsfactory-gen/glayout/primitives/via_gen.py b/openfasoc/generators/glayout/glayout/flow/primitives/via_gen.py similarity index 97% rename from openfasoc/generators/gdsfactory-gen/glayout/primitives/via_gen.py rename to openfasoc/generators/glayout/glayout/flow/primitives/via_gen.py index 2b6d2d024..1e9e722b0 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/primitives/via_gen.py +++ b/openfasoc/generators/glayout/glayout/flow/primitives/via_gen.py @@ -2,12 +2,12 @@ from gdsfactory.component import Component from gdsfactory.components.rectangle import rectangle from pydantic import validate_arguments -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from math import floor from typing import Optional, Union -from glayout.pdk.util.comp_utils import evaluate_bbox, prec_array, to_float, move, prec_ref_center, to_decimal -from glayout.pdk.util.port_utils import rename_ports_by_orientation, print_ports -from glayout.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_array, to_float, move, prec_ref_center, to_decimal +from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, print_ports +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid from decimal import Decimal from typing import Literal diff --git a/openfasoc/generators/gdsfactory-gen/glayout/routing/L_route.py b/openfasoc/generators/glayout/glayout/flow/routing/L_route.py similarity index 90% rename from openfasoc/generators/gdsfactory-gen/glayout/routing/L_route.py rename to openfasoc/generators/glayout/glayout/flow/routing/L_route.py index 25eca738b..90db40541 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/routing/L_route.py +++ b/openfasoc/generators/glayout/glayout/flow/routing/L_route.py @@ -1,11 +1,11 @@ from gdsfactory.cell import cell from gdsfactory.component import Component from gdsfactory.port import Port -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union -from glayout.primitives.via_gen import via_stack, via_array -from glayout.pdk.util.comp_utils import evaluate_bbox, align_comp_to_port, to_decimal, to_float, prec_ref_center, get_primitive_rectangle -from glayout.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, print_ports, assert_port_manhattan, assert_ports_perpindicular +from glayout.flow.primitives.via_gen import via_stack, via_array +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, align_comp_to_port, to_decimal, to_float, prec_ref_center, get_primitive_rectangle +from glayout.flow.pdk.util.port_utils import rename_ports_by_orientation, rename_ports_by_list, print_ports, assert_port_manhattan, assert_ports_perpindicular from decimal import Decimal diff --git a/openfasoc/generators/glayout/glayout/flow/routing/__init__.py b/openfasoc/generators/glayout/glayout/flow/routing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openfasoc/generators/gdsfactory-gen/glayout/routing/c_route.py b/openfasoc/generators/glayout/glayout/flow/routing/c_route.py similarity index 83% rename from openfasoc/generators/gdsfactory-gen/glayout/routing/c_route.py rename to openfasoc/generators/glayout/glayout/flow/routing/c_route.py index 1e79c1c2e..7f7bcf18b 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/routing/c_route.py +++ b/openfasoc/generators/glayout/glayout/flow/routing/c_route.py @@ -1,21 +1,24 @@ from gdsfactory.cell import cell from gdsfactory.component import Component from gdsfactory.port import Port -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional, Union from math import isclose -from glayout.primitives.via_gen import via_stack -from gdsfactory.routing.route_quad import route_quad +from glayout.flow.primitives.via_gen import via_stack +from glayout.flow.routing.straight_route import straight_route from gdsfactory.components.rectangle import rectangle -from glayout.pdk.util.comp_utils import evaluate_bbox, get_primitive_rectangle -from glayout.pdk.util.port_utils import add_ports_perimeter, rename_ports_by_orientation, rename_ports_by_list, print_ports, set_port_width, set_port_orientation, get_orientation +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, get_primitive_rectangle +from glayout.flow.pdk.util.port_utils import add_ports_perimeter, rename_ports_by_orientation, rename_ports_by_list, print_ports, set_port_width, set_port_orientation, get_orientation from pydantic import validate_arguments from gdsfactory.snap import snap_to_grid @validate_arguments -def __fill_empty_viastack__macro(pdk: MappedPDK, glayer: str, size: tuple[float,float]) -> Component: - """returns a rectangle with ports that pretend to be viastack ports""" +def __fill_empty_viastack__macro(pdk: MappedPDK, glayer: str, size: Optional[tuple[float,float]]=None) -> Component: + """returns a rectangle with ports that pretend to be viastack ports + by default creates a rectangle with size double the min width of the glayer""" + if size is None: + size = pdk.snap_to_2xgrid([2*pdk.get_grule(glayer)["min_width"],2*pdk.get_grule(glayer)["min_width"]]) comp = rectangle(size=size,layer=pdk.get_glayer(glayer),centered=True) return rename_ports_by_orientation(rename_ports_by_list(comp,replace_list=[("e","top_met_")])).flatten() @@ -32,7 +35,8 @@ def c_route( e2glayer: Optional[str] = None, cglayer: Optional[str] = None, viaoffset: Optional[Union[bool,tuple[Optional[bool],Optional[bool]]]]=(True,True), - fullbottom: Optional[bool] = False + fullbottom: Optional[bool] = False, + debug=False ) -> Component: """creates a C shaped route between two Ports. @@ -57,6 +61,9 @@ def c_route( - None means center (no offset) ****NOTE: viaoffset pushes both vias towards each other slightly """ + if debug: + pass + #import pdb; pdb.set_trace() # error checking and figure out args if round(edge1.orientation) % 90 or round(edge2.orientation) % 90: raise ValueError("Ports must be vertical or horizontal") @@ -80,8 +87,9 @@ def c_route( croute = Component() viastack1 = via_stack(pdk,e1glayer,cglayer,fullbottom=fullbottom,assume_bottom_via=True) viastack2 = via_stack(pdk,e2glayer,cglayer,fullbottom=fullbottom,assume_bottom_via=True) - if e1glayer == e2glayer: - pass + if e1glayer==cglayer and e2glayer==cglayer: + viastack1 = __fill_empty_viastack__macro(pdk,e1glayer) + viastack2 = __fill_empty_viastack__macro(pdk,e2glayer) elif e1glayer == cglayer: viastack1 = __fill_empty_viastack__macro(pdk,e1glayer,size=evaluate_bbox(viastack2)) elif e2glayer == cglayer: @@ -163,6 +171,8 @@ def c_route( me2.movex(0-viastack2.xmax).movey(via_flush2) me1, me2 = (me1, me2) if (me1.origin[1] > me2.origin[1]) else (me2, me1) route_ports = [me1.ports["top_met_N"],me2.ports["top_met_S"]] + fix_connection_direction = "E" + fix_ports = [me1.ports["top_met_E"],me2.ports["top_met_E"]] elif round(edge1.orientation) == 180:# facing west me1.move(destination=e1_extension.ports["e_W"].center) me2.move(destination=e2_extension.ports["e_W"].center) @@ -171,6 +181,8 @@ def c_route( me2.movex(viastack2.xmax).movey(via_flush2) me1, me2 = (me1, me2) if (me1.origin[1] > me2.origin[1]) else (me2, me1) route_ports = [me1.ports["top_met_N"],me2.ports["top_met_S"]] + fix_connection_direction = "E" + fix_ports = [me1.ports["top_met_E"],me2.ports["top_met_E"]] elif round(edge1.orientation) == 270:# facing south me1.move(destination=e1_extension.ports["e_S"].center) me2.move(destination=e2_extension.ports["e_S"].center) @@ -179,6 +191,8 @@ def c_route( me2.movey(viastack2.xmax).movex(via_flush2) me1, me2 = (me1, me2) if (me1.origin[0] > me2.origin[0]) else (me2, me1) route_ports = [me1.ports["top_met_E"],me2.ports["top_met_W"]] + fix_connection_direction = "N" + fix_ports = [me1.ports["top_met_N"],me2.ports["top_met_N"]] else:#facing north me1.move(destination=e1_extension.ports["e_N"].center) me2.move(destination=e2_extension.ports["e_N"].center) @@ -187,13 +201,22 @@ def c_route( me2.movey(0-viastack2.xmax).movex(via_flush2) me1, me2 = (me1, me2) if (me1.origin[0] > me2.origin[0]) else (me2, me1) route_ports = [me1.ports["top_met_E"],me2.ports["top_met_W"]] + fix_connection_direction = "N" + fix_ports = [me1.ports["top_met_N"],me2.ports["top_met_N"]] # connect extensions, add ports, return croute << e1_extension_comp croute << e2_extension_comp if cwidth: route_ports = [set_port_width(port_,cwidth) for port_ in route_ports] route_ports[0].width = route_ports[1].width = max(route_ports[0].width, route_ports[1].width) - cconnection = croute << route_quad(route_ports[0],route_ports[1],layer=pdk.get_glayer(cglayer)) + route_port0 = route_ports[0].copy() + route_port1 = route_ports[1].copy() + route_port0.layer = route_port1.layer = pdk.get_glayer(cglayer) + cconnection = croute << straight_route(pdk, route_port0,route_port1,glayer1=cglayer,glayer2=cglayer) + for _port in fix_ports: + port2 = cconnection.ports["route_"+fix_connection_direction] + port2.layer = _port.layer = pdk.get_glayer(cglayer) + croute << straight_route(pdk, _port, port2, glayer1=cglayer,glayer2=cglayer) for i,port_to_add in enumerate(route_ports): orta = get_orientation(port_to_add.orientation) route_ports[i] = set_port_orientation(port_to_add, orta) diff --git a/openfasoc/generators/glayout/glayout/flow/routing/smart_route.py b/openfasoc/generators/glayout/glayout/flow/routing/smart_route.py new file mode 100644 index 000000000..0443f89f9 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/routing/smart_route.py @@ -0,0 +1,311 @@ +import warnings +from typing import Optional, Union + +from gdsfactory import Component, ComponentReference +from gdsfactory.port import Port +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.util.comp_utils import align_comp_to_port, movex +from glayout.flow.pdk.util.port_utils import ( + assert_port_manhattan, + ports_inline, + ports_parallel, +) +from glayout.flow.primitives.via_gen import via_stack +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 + + +def smart_route( + pdk: MappedPDK, + edge1: Port, + edge2: Port, + ref_comp: Optional[Union[Component, ComponentReference]]=None, + top_comp: Optional[Union[Component, ComponentReference]]=None, + **kwargs +) -> Component: + # error checks + assert_port_manhattan([edge1,edge2]) + # determine route type based on preconfiged route utils + if top_comp is not None and ref_comp is not None: + if ref_comp.info.get("route_genid") is not None: + try: + if ref_comp.info["route_genid"] == "two_transistor_interdigitized": + return generic_route_two_transistor_interdigitized(pdk, edge1, edge2, top_comp) + if ref_comp.info["route_genid"] == "four_transistor_interdigitized": + return generic_route_four_transistor_interdigitized(pdk, edge1, edge2, top_comp) + if ref_comp.info["route_genid"] == "common_centroid_ab_ba": + return generic_route_ab_ba_common_centroid(pdk, edge1, edge2, top_comp) + except ValueError: + warnings.warn("Attempted a specialized smart route, but failed. Now attempting general smart route...") + # determine route type based on port orientation and distance + if ports_parallel(edge1,edge2): + # croute or straightroute + if ports_inline(edge1,edge2): + return straight_route(pdk, edge1, edge2, **kwargs) + else: + return c_route(pdk, edge1, edge2, **kwargs) + else: + return L_route(pdk, edge1, edge2, **kwargs) + + +# AorB_source,gate,or drain +def parse_port_name(pname: str) -> str: + """Parse a port name to extract the device (A or B) and pin (source, drain, or gate) + returning just that (without directions NESW) + Args: + pname (str): The port name to get extract device_pin + Returns: + str: A string containing the component and pin type separated by an underscore. + """ + comp = str() + pin = str() + for part in pname.split("_"): + if part=="A" or part=="B": + comp = part + if part=="source" or part=="drain" or part=="gate": + pin=part + return comp+"_"+pin + + +def check_route(name1, name2, pin1, pin2) -> bool: + # check if this routes between the 2 pins + cond1 = name1==pin1 and name2==pin2 + cond2 = name2==pin1 and name1==pin2 + return cond1 or cond2 + + +def generic_route_two_transistor_interdigitized( + pdk: MappedPDK, + edge1: Port, + edge2: Port, + top_comp: Union[Component, ComponentReference] +) -> Component: + def compensate_for_croutes(top_comp, sample_port: Port, LeftSide: bool, extend_to: Port): + # top_comp is Component to modify, sample_port is a port name for refernce (we dont know what the prefix should be) side is either west (left) or east + # adds a metal extension and changes the ports to compensate for the fact that there will be a croute on that side + # extend_to should be the outside port of croute + basename = sample_port.name.rstrip("NESW").rstrip("_") + basename = basename.removesuffix("source").removesuffix("drain").removesuffix("gate").rstrip("_") + basename = basename.removesuffix("A").removesuffix("B") + direction = "_W" if LeftSide else "_E" + #import pdb; pdb.set_trace() + for dev in ["A_","B_"]: + specbase = basename + dev + for pin in ["source","drain","gate"]: + portcorrection = top_comp.ports[specbase+pin+direction+"_private"] + rt = top_comp << straight_route(pdk, portcorrection, extend_to, glayer2=pdk.layer_to_glayer(portcorrection.layer)) + top_comp.ports[specbase+pin+direction] = rt.ports["route"+direction] + top_comp.ports[specbase+pin+direction].name = specbase+pin+direction + # no return (modifies the top_comp) + def exchange_ports(top_comp, edge: Port, direction: str) -> Port: + # gives port which is same except a different edge N,E,S,W + return top_comp.ports[edge.name.rstrip("NESW")+direction+"_private"] + #glayer1 = pdk.layer_to_glayer(edge1.layer) + glayer2 = pdk.layer_to_glayer(edge2.layer) + name1 = parse_port_name(edge1.name) + name2 = parse_port_name(edge2.name) + # order names so that A is first (if only one A) + if "A" in name2 and not("A" in name1): + name1, name2 = name2, name1 + # trivial routes + samecomp = any(l in name1 and l in name2 for l in ["A","B"]) + samepin = any(l in name1 and l in name2 for l in ["gate","source","drain"]) + if samecomp and samepin: + return Component() + # easy routes 3/15 + straight_route_width = 1 if edge1.width > 1 else edge1.width + if check_route(name1,name2,"A_source","B_source"): + edge = edge1 if "B" in edge1.name else edge2 + return align_comp_to_port(via_stack(pdk,"met1",glayer2),exchange_ports(top_comp,edge,"W"),alignment=("r","c"),rtr_comp_ref=False) + if check_route(name1,name2,"A_gate","B_gate"): + return straight_route(pdk,exchange_ports(top_comp,edge1,"S"),exchange_ports(top_comp,edge2,"S"),width=straight_route_width) + if check_route(name1,name2,"A_drain","B_drain"): + edge = edge1 if "A" in edge1.name else edge2 + return align_comp_to_port(via_stack(pdk,"met1",glayer2),exchange_ports(top_comp,edge,"E"),alignment=("l","c"),rtr_comp_ref=False) + # easy self routes (A->A or B->B, and source<->drain) 2/15 (5/15) + if check_route(name1,name2,"A_drain","A_source"): + edge = edge1 if "drain" in edge1.name else edge2 + return align_comp_to_port(via_stack(pdk,"met1",glayer2),exchange_ports(top_comp,edge,"W"),alignment=("r","c"),rtr_comp_ref=False) + if check_route(name1,name2,"B_drain","B_source"): + edge = edge1 if "source" in edge1.name else edge2 + return align_comp_to_port(via_stack(pdk,"met1",glayer2),exchange_ports(top_comp,edge,"E"),alignment=("l","c"),rtr_comp_ref=False)# B is higher + # hard self route (A->A or B->B, and source or drain -> gate) 4/15 (9/15) + viaoffset = not(check_route(name1,name2,"A_gate","A_drain") or check_route(name1,name2,"B_gate","B_drain")) + edge1, edge2 = (edge2, edge1) if ("drain" in edge2.name or "source" in edge2.name) else (edge1, edge2) + width2 = edge2.width + if check_route(name1,name2,"A_gate","A_drain") or check_route(name1,name2,"A_gate","A_source"): + rt = c_route(pdk,exchange_ports(top_comp,edge1,"W"),exchange_ports(top_comp,edge2,"W"),viaoffset=(viaoffset,True),width2=width2) # A gets W + compensate_for_croutes(top_comp,edge1,True,rt.ports["con_N"]) + return rt + if check_route(name1,name2,"B_gate","B_drain") or check_route(name1,name2,"B_gate","B_source"): + rt = c_route(pdk,exchange_ports(top_comp,edge1,"E"),exchange_ports(top_comp,edge2,"E"),viaoffset=(viaoffset,True),width2=width2) # B gets E + compensate_for_croutes(top_comp,edge1,False,rt.ports["con_N"]) + return rt + # inter transistor routes going to the gate of A or B 4/15 (13/15) + if check_route(name1,name2,"A_gate","B_drain") or check_route(name1,name2,"A_gate","B_source"): + rt = c_route(pdk,exchange_ports(top_comp,edge1,"W"),exchange_ports(top_comp,edge2,"W"),viaoffset=(not(check_route(name1,name2,"A_gate","B_drain")),True),width2=width2) # A_gate gets W + compensate_for_croutes(top_comp,edge1,True,rt.ports["con_N"]) + return rt + if check_route(name1,name2,"B_gate","A_drain") or check_route(name1,name2,"B_gate","A_source"): + rt = c_route(pdk,exchange_ports(top_comp,edge1,"E"),exchange_ports(top_comp,edge2,"E"),viaoffset=(not(check_route(name1,name2,"B_gate","A_drain")),True),width2=width2) # B_gate gets E + compensate_for_croutes(top_comp,edge1,False,rt.ports["con_N"]) + return rt + # inter transistor routes going to from s or d to s or d 2/15 (15/15) + if check_route(name1,name2,"A_source","B_drain"): + edge = edge1 if "drain" in edge1.name else edge2 + return align_comp_to_port(via_stack(pdk,"met1",glayer2),exchange_ports(top_comp,edge,"W"),alignment=("r","c"),rtr_comp_ref=False) + if check_route(name1,name2,"A_drain","B_source"): + edge = edge1 if "drain" in edge1.name else edge2 + return align_comp_to_port(via_stack(pdk,"met1",glayer2),exchange_ports(top_comp,edge,"E"),alignment=("l","c"),rtr_comp_ref=False) + raise ValueError("You picked a port that smart_route with interdigitized 2 transistor does not support") + + +def generic_route_four_transistor_interdigitized( + pdk: MappedPDK, + edge1: Port, + edge2: Port, + top_comp: Union[Component, ComponentReference] +) -> Component: + def check_port(pname: str) -> bool: + # check that this is a source, drain, or gate + # returns false if the port is source drain or gate + pname = pname.rstrip("NESW_") + pin = pname.split("_")[-1] + return not(pin=="source" or pin=="gate" or pin=="drain") + def strip_portname(pname: str) -> str: + # removes upto the two transistor interdigitzed stuff + pname = pname.rstrip("NESW_") + pname = pname.removesuffix("source").removesuffix("drain").removesuffix("gate") + pname = pname.rstrip("_").removesuffix("A").removesuffix("B").rstrip("_") + return pname + def exchange_ports(top_comp, edge: Port, direction: str) -> Port: + # gives port which is same except a different edge N,E,S,W + return top_comp.ports[edge.name.rstrip("NESW")+direction+"_private"] + def parse_port_name(pname: str) -> str: + comp = str() + pin = str() + topbot = str() + for part in pname.split("_"): + if part=="A" or part=="B": + comp = part + if part=="source" or part=="drain" or part=="gate": + pin=part + if part=="top" or part=="bottom": + topbot = part + return topbot+"_"+comp+"_"+pin + def compensate_for_croutes(top_comp, sample_port: Port, LeftSide: bool, extend_to: Port): + # top_comp is Component to modify, sample_port is a port name for refernce (we dont know what the prefix should be) side is either west (left) or east + # adds a metal extension and changes the ports to compensate for the fact that there will be a croute on that side + # extend_to should be the outside port of croute + basename = sample_port.name.rstrip("NESW").rstrip("_") + basename = basename.removesuffix("source").removesuffix("drain").removesuffix("gate").rstrip("_") + basename = basename.removesuffix("A").removesuffix("B") + direction = "_W" if LeftSide else "_E" + #import pdb; pdb.set_trace() + for dev in ["A_","B_"]: + specbase = basename + dev + for pin in ["source","drain","gate"]: + portcorrection = top_comp.ports[specbase+pin+direction+"_private"] + rt = top_comp << straight_route(pdk, portcorrection, extend_to, glayer2=pdk.layer_to_glayer(portcorrection.layer)) + top_comp.ports[specbase+pin+direction] = rt.ports["route"+direction] + top_comp.ports[specbase+pin+direction].name = specbase+pin+direction + # check that this function supports the ports specified + cond1 = check_port(edge1.name) or check_port(edge2.name) + cond2 = strip_portname(edge1.name).split("_")[-1] != strip_portname(edge2.name).split("_")[-1] + name1 = parse_port_name(edge1.name) + name2 = parse_port_name(edge2.name) + width1 = edge1.width + width2 = edge2.width + if "A" in name2 and not("A" in name1): + name1, name2 = name2, name1 + if cond1: + raise ValueError("You picked a port that smart_route with interdigitized 4 transistor does not support") + elif cond2: + # do your code here + + # if check_route("") + + raise ValueError("these ports will be supported soon") + # print(f'\nname1: {name1}, name2: {name2}\n"top_A_source", "bottom_A_source"\n') + + print('\n result of check_route: ', check_route(name1, name2, "top_A_source", "bottom_A_source")) + if check_route(name1, name2, "top_A_drain", "bottom_A_drain"): + rt = c_route(pdk, exchange_ports(top_comp, edge1, "W"), exchange_ports(top_comp, edge2, "W"), viaoffset=(True, True), width1=width1, width2=width2) + compensate_for_croutes(top_comp, edge1, True, rt.ports["con_N"]) + return rt + if check_route(name1, name2, "top_A_drain", "bottom_A_source"): + # print('\n\nin second if condition\n\n') + rt = c_route(pdk, exchange_ports(top_comp, edge1, "W"), exchange_ports(top_comp, edge2, "W"), viaoffset=(True, True), width1=width1, width2=width2) + compensate_for_croutes(top_comp, edge1, True, rt.ports["con_N"]) + return rt + if check_route(name1, name2, "top_A_source", "bottom_A_source"): + # print('\n\nin third if condition\n\n') + rt = c_route(pdk, exchange_ports(top_comp, edge1, "W"), exchange_ports(top_comp, edge2, "W"), viaoffset=(True, True), width1=width1, width2=width2, extension=4*width1) + compensate_for_croutes(top_comp, edge1, True, rt.ports["con_N"]) + return rt + if check_route(name1, name2, "top_A_source", "bottom_A_drain"): + # print('\n\nin fourth if condition\n\n') + rt = c_route(pdk, exchange_ports(top_comp, edge1, "W"), exchange_ports(top_comp, edge2, "W"), viaoffset=(True, True), width1=width1, width2=width2, extension=4*width1) + compensate_for_croutes(top_comp, edge1, True, rt.ports["con_N"]) + return rt + else: + # else return 2 tran route + return generic_route_two_transistor_interdigitized(pdk, edge1, edge2, top_comp) + + + +def generic_route_ab_ba_common_centroid( + pdk: MappedPDK, + edge1: Port, + edge2: Port, + top_comp: Union[Component, ComponentReference] +) -> Component: + # TODO: implement + name1, name2 = parse_port_name(edge1.name), parse_port_name(edge2.name) + width1 = edge1.width + # order names so that A is first (if only one A) + if "A" in name2 and not("A" in name1): + name1, name2 = name2, name1 + # same device routes (A->A or B->B) (6/15) + if check_route(name1,name2,"A_source","A_gate"): + return straight_route(pdk, top_comp.ports["A_source_E_private"],top_comp.ports["A_gate_route_con_N"],via2_alignment=("right","bottom")) + if check_route(name1,name2,"A_drain","A_gate"): + return straight_route(pdk, top_comp.ports["A_drain_E_private"],top_comp.ports["A_gate_route_con_N"],via2_alignment=("right","top")) + if check_route(name1,name2,"A_source","A_drain"): + straight_route(pdk, top_comp.ports["br_multiplier_0_source_N"],top_comp.ports["br_multiplier_0_drain_S"],width=min(width1,1)) + return straight_route(pdk, top_comp.ports["tl_multiplier_0_source_S"],top_comp.ports["tl_multiplier_0_drain_N"],width=min(width1,1)) + if check_route(name1,name2,"B_source","B_gate"): + return straight_route(pdk, top_comp.ports["B_source_W_private"],top_comp.ports["B_gate_route_con_N"],via2_alignment=("left","bottom")) + if check_route(name1,name2,"B_drain","B_gate"): + return straight_route(pdk, top_comp.ports["B_drain_W_private"],top_comp.ports["B_gate_route_con_N"],via2_alignment=("left","top")) + if check_route(name1,name2,"B_source","B_drain"): + top_comp << straight_route(pdk, top_comp.ports["tr_multiplier_0_source_S"],top_comp.ports["tr_multiplier_0_drain_N"],width=min(width1,1)) + return straight_route(pdk, top_comp.ports["bl_multiplier_0_source_N"],top_comp.ports["bl_multiplier_0_drain_S"],width=min(width1,1)) + # A_src/drain->B_gate or B_src/drain->A_gate (4/15) + if check_route(name1,name2,"A_source","B_gate"): + return straight_route(pdk, top_comp.ports["A_source_W_private"],top_comp.ports["B_gate_route_con_N"],via2_alignment=("left","top")) + if check_route(name1,name2,"A_drain","B_gate"): + return straight_route(pdk, top_comp.ports["A_drain_W_private"],top_comp.ports["B_gate_route_con_N"],via2_alignment=("left","bottom")) + if check_route(name1,name2,"B_source","A_gate"): + return straight_route(pdk, top_comp.ports["B_source_E_private"],top_comp.ports["A_gate_route_con_N"],via2_alignment=("right","top")) + if check_route(name1,name2,"B_drain","A_gate"): + return straight_route(pdk, top_comp.ports["B_drain_E_private"],top_comp.ports["A_gate_route_con_N"],via2_alignment=("right","bottom")) + # A_src/drain->B_src or A_src/drain->B_drain (4/15) + if check_route(name1,name2,"A_source","B_source"): + return straight_route(pdk, top_comp.ports["tl_multiplier_0_source_E"],top_comp.ports["tr_multiplier_0_source_W"]) + if check_route(name1,name2,"A_drain","B_source"): + portmv1 = top_comp.ports["tl_multiplier_0_drain_E"].copy() + return straight_route(pdk, top_comp.ports["tl_multiplier_0_drain_E"],movex(portmv1,2*pdk.get_grule(pdk.layer_to_glayer(portmv1.layer))["min_separation"])) + if check_route(name1,name2,"A_source","B_drain"): + portmv1 = top_comp.ports["tr_multiplier_0_drain_W"].copy() + return straight_route(pdk, top_comp.ports["tr_multiplier_0_drain_W"],movex(portmv1,-2*pdk.get_grule(pdk.layer_to_glayer(portmv1.layer))["min_separation"])) + if check_route(name1,name2,"A_drain","B_drain"): + portmv1 = top_comp.ports["bl_mutliplier_0_drain_N"].copy() + portmv2 = top_comp.ports["br_multiplier_0_drain_N"].copy() + top_comp << straight_route(pdk, movex(portmv1,-portmv1.width/2), top_comp.ports["tl_multiplier_0_drain_S"],width=width1) + return straight_route(pdk, movex(portmv2,portmv2.width/2),top_comp.ports["tr_multiplier_0_drain_S"]) + # A_gate -> B_gate (1/15) + if check_route(name1,name2,"A_gate","B_gate"): + return straight_route(pdk,top_comp.ports["br_multiplier_0_gate_W"],top_comp.ports["bl_multiplier_0_gate_E"]) + raise ValueError("You picked a port that smart_route with ab_ba_common_centroid does not support") diff --git a/openfasoc/generators/gdsfactory-gen/glayout/routing/straight_route.py b/openfasoc/generators/glayout/glayout/flow/routing/straight_route.py similarity index 93% rename from openfasoc/generators/gdsfactory-gen/glayout/routing/straight_route.py rename to openfasoc/generators/glayout/glayout/flow/routing/straight_route.py index ba8bccff3..75cd5f32d 100644 --- a/openfasoc/generators/gdsfactory-gen/glayout/routing/straight_route.py +++ b/openfasoc/generators/glayout/glayout/flow/routing/straight_route.py @@ -1,12 +1,12 @@ from gdsfactory.cell import cell from gdsfactory.component import Component from gdsfactory.port import Port -from glayout.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.mappedpdk import MappedPDK from typing import Optional -from glayout.primitives.via_gen import via_stack, via_array +from glayout.flow.primitives.via_gen import via_stack, via_array from gdsfactory.components.rectangle import rectangle -from glayout.pdk.util.comp_utils import evaluate_bbox, align_comp_to_port -from glayout.pdk.util.port_utils import assert_port_manhattan, set_port_orientation, add_ports_perimeter +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, align_comp_to_port +from glayout.flow.pdk.util.port_utils import assert_port_manhattan, set_port_orientation, add_ports_perimeter from gdstk import rectangle as primitive_rectangle diff --git a/openfasoc/generators/gdsfactory-gen/glayout/spice/__init__.py b/openfasoc/generators/glayout/glayout/flow/spice/__init__.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/spice/__init__.py rename to openfasoc/generators/glayout/glayout/flow/spice/__init__.py diff --git a/openfasoc/generators/gdsfactory-gen/glayout/spice/netlist.py b/openfasoc/generators/glayout/glayout/flow/spice/netlist.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/glayout/spice/netlist.py rename to openfasoc/generators/glayout/glayout/flow/spice/netlist.py diff --git a/openfasoc/generators/glayout/glayout/llm/__init__.py b/openfasoc/generators/glayout/glayout/llm/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openfasoc/generators/glayout/glayout/llm/glayoutmodel.py b/openfasoc/generators/glayout/glayout/llm/glayoutmodel.py new file mode 100644 index 000000000..110c4fabf --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/glayoutmodel.py @@ -0,0 +1,127 @@ +import pandas as pd +import torch +from transformers import BertTokenizer, BertModel + +# Load the pre-trained BERT model and tokenizer +tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') +model = BertModel.from_pretrained('bert-base-uncased') + +# Load the training data +train_data = pd.read_csv('train.csv') + +# Preprocess the data +input_ids = [] +attention_masks = [] +labels = [] + +for text, label in train_data.values: + inputs = tokenizer.encode_plus( + text, + add_special_tokens=True, + max_length=512, + padding='max_length', + truncation=True, + return_attention_mask=True, + return_tensors='pt' + ) + input_ids.append(inputs['input_ids'].flatten()) + attention_masks.append(inputs['attention_mask'].flatten()) + labels.append(label) + +# Convert lists to tensors +input_ids = torch.tensor(input_ids) +attention_masks = torch.tensor(attention_masks) +labels = torch.tensor(labels) + +# Define a custom dataset class +class GlayoutDataset(torch.utils.data.Dataset): + def __init__(self, input_ids, attention_masks, labels): + self.input_ids = input_ids + self.attention_masks = attention_masks + self.labels = labels + + def __len__(self): + return len(self.input_ids) + + def __getitem__(self, idx): + input_ids = self.input_ids[idx] + attention_masks = self.attention_masks[idx] + labels = self.labels[idx] + return { + 'input_ids': input_ids, + 'attention_mask': attention_masks, + 'labels': labels + } + +# Create a dataset and data loader +dataset = GlayoutDataset(input_ids, attention_masks, labels) +data_loader = torch.utils.data.DataLoader(dataset, batch_size=32, shuffle=True) + + +# Define a custom model class +class GlayoutModel(BertModel): + def __init__(self, num_labels): + super(GlayoutModel, self).__init__() + self.num_labels = num_labels + self.dropout = torch.nn.Dropout(0.1) + self.fc = torch.nn.Linear(self.config.hidden_size, num_labels) + + def forward(self, input_ids, attention_mask): + outputs = super(GlayoutModel, self).forward( + input_ids=input_ids, + attention_mask=attention_mask + ) + pooler_output = outputs.pooler_output + pooler_output = self.dropout(pooler_output) + outputs = self.fc(pooler_output) + return outputs + +# Initialize the custom model +model = GlayoutModel(num_labels=len(glayoutactions)) + +# Fine-tune the model +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +criterion = torch.nn.CrossEntropyLoss() +optimizer = torch.optim.Adam(model.parameters(), lr=1e-5) + +for epoch in range(5): + model.train() + total_loss = 0 + for batch in data_loader: + input_ids = batch['input_ids'].to(device) + attention_mask = batch['attention_mask'].to(device) + labels = batch['labels'].to(device) + optimizer.zero_grad() + outputs = model(input_ids, attention_mask) + loss = criterion(outputs, labels) + loss.backward() + optimizer.step() + total_loss += loss.item() + print(f'Epoch {epoch+1}, Loss: {total_loss / len(data_loader)}') + +# Evaluate the model +model.eval() +eval_results = [] +with torch.no_grad(): + for batch in data_loader: + input_ids = batch['input_ids'].to(device) + attention_mask = batch['attention_mask'].to(device) + labels = batch['labels'].to(device) + outputs = model(input_ids, attention_mask) + logits = outputs.detach().cpu().numpy() + label_ids = labels.detach().cpu().numpy() + eval_results.extend(zip(logits, label_ids)) + +# Use the fine-tuned model to generate strict syntax output +def generate_syntax(text): + input_ids = tokenizer.encode(text, return_tensors='pt') + attention_mask = tokenizer.encode(text, return_tensors='pt', max_length=512, padding='max_length', truncation=True) + outputs = model(input_ids, attention_mask) + logits = outputs.detach().cpu().numpy() + predicted_label = GlayoutActions(logits.argmax()) + return predicted_label + +# Example usage: +text = "This is a sample input text" +syntax_output = generate_syntax(text) +print(syntax_output) diff --git a/openfasoc/generators/glayout/glayout/llm/manage_data.py b/openfasoc/generators/glayout/glayout/llm/manage_data.py new file mode 100644 index 000000000..15845a86f --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/manage_data.py @@ -0,0 +1,144 @@ +import json +import warnings +from pathlib import Path +from typing import List, Tuple, Union +from langchain_community.document_loaders import DirectoryLoader, TextLoader +from langchain_community.vectorstores import Chroma +from langchain_community.embeddings import HuggingFaceEmbeddings + + +def get_glayout_context() -> str: + contextmdfile = Path(__file__).resolve().parent / "syntax_data/GlayoutStrictSyntax.md" + loader = TextLoader(contextmdfile) + return loader.load()[0].page_content + + +def load_labeled_syntax_data_json( + json_file_path: Union[str, Path], + convo_dir_path: Union[str, Path], + fail_on_error: bool = True, +) -> List[Tuple[str, str]]: + """Load all labeled data examples from a JSON file by reading LLM prompt + and extracting strict syntax from corresponding '.convo' files. + Args: + json_file_path (str or Path): The path to the JSON file. + convo_dir_path (str or Path): The path to the directory containing convo files for training + fail_on_error (bool): if False, issue warnings if an individual example raises an Exception + Raises: + FileNotFoundError: If a '.convo' file, the convo_dir_path or json_file_path do not exist + KeyError: if the JSON file does not correspond to the expected form + jsonfile[data] must be a list of examples containing an LLMprompt and NLPfilename + Returns: + list of tuple: A list of tuples containing information about each example. + Each tuple consists of: + - str: The LLM prompt. + - str: The text found in the NLP file. + """ + # process the convo dir path + convo_dir_path = Path(convo_dir_path).resolve() + if not convo_dir_path.is_dir(): + raise FileNotFoundError(f"convo directory not found {convo_dir_path}") + # this requires manage_data.py to be in the llm folder + # Load JSON data + json_file_path = Path(json_file_path).resolve() + if not json_file_path.is_file(): + raise FileNotFoundError(f"could not find JSON file {json_file_path}") + with open(json_file_path, "r") as file: + data = json.load(file) + examples = data.get("data") + if examples is None: + raise KeyError( + f"data not found in JSON file {json_file_path}, ensure 'data' keyword is used" + ) + # loop through examples and append to results + results = list() + for example in examples: + try: + llm_prompt = example.get("LLMprompt") + if llm_prompt is None: + raise KeyError(f"LLMprompt not found in JSON data {example}") + # get the name of the strict syntax convo corresponding to this example + nlp_filename = example.get("NLPfilename") + if nlp_filename is None: + raise KeyError(f"NLPfilename not found in JSON data {example}") + convo_filename = f"{nlp_filename.strip()}" + if not convo_filename.endswith(".convo"): + convo_filename += ".convo" + # get the .convo file path and check that it exists + convo_file_path = Path(convo_dir_path).resolve() / convo_filename + if not convo_file_path.is_file(): + raise FileNotFoundError(f"Convo file '{convo_file_path}' not found.") + # Read text from convo file + with convo_file_path.open("r") as nlp_file: + nlp_text = nlp_file.read() + results.append((llm_prompt, nlp_text)) + except (FileNotFoundError, KeyError) as faulty_example_error: + if fail_on_error: + raise faulty_example_error + print(f"\n\nan exception was encounterd when processing {example}\n") + warnings.warn(f"{faulty_example_error}") + return results + + +def load_all_labeled_syntax_data_json() -> List[Tuple[str, str]]: + """look in syntax_data folder for any json files + and run load_labeled_syntax_data_json on those files + Returns: + list[tuple]: all results from running load_labeled_syntax_data_json on all json files + """ + # Path to the directory containing JSON files + llm_dir = Path(__file__).resolve().parent + json_dir = llm_dir / "syntax_data" + convo_dir = json_dir / "convos" + if not json_dir.is_dir(): + raise FileNotFoundError( + "Could not find syntax_data directory within llm subpackage of glayout" + ) + # Iterate over all JSON files in the directory + all_results = list() + for json_file_path in json_dir.glob("*.json"): + all_results.extend(load_labeled_syntax_data_json(json_file_path, convo_dir, False)) + return all_results + + + +class RAGdb: + """ + A class to create and manage a vector database for the RAG data using ChromaDB. + + Attributes: + chroma_client (Client): The ChromaDB client used for managing the vector database. + collection_name (str): The name of the collection used in ChromaDB. + collection (Collection): The vector database + """ + + def __init__(self, rag_data_dir: Union[str, Path]): + """Initializes the RAGdb instance with a ChromaDB collection""" + # error checking + rag_data_dir = Path(rag_data_dir).resolve() + if not rag_data_dir.is_dir(): + raise FileNotFoundError(f"could not find RAG data directory {rag_data_dir}") + # load RAG data + self.documents = DirectoryLoader(str(rag_data_dir), glob="*.md").load() + # create vector db + embeddings_model_name = "sentence-transformers/all-MiniLM-L6-v2" + embeddings = HuggingFaceEmbeddings(model_name=embeddings_model_name) + self.vectordb = Chroma.from_documents(documents=self.documents,embedding=embeddings) + + + def query(self, query_text: str, k: int=4): + """ + Queries the vector database to find the top-k most similar vectors to the given query text. + Args: + query_text (str): The text to query. + Returns: + List: The list of top-k most similar docs. + """ + rawdocs = self.vectordb.similarity_search(query=query_text,k=k) + rawtxt = list() + for doc in rawdocs: + rawtxt.append(doc.page_content) + return rawtxt + +RAGvecdb = RAGdb(Path(__file__).resolve().parent / "rag_data") +#print(vecdb.query("currentmir")) \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/bjt.md b/openfasoc/generators/glayout/glayout/llm/rag_data/bjt.md new file mode 100644 index 000000000..692b87000 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/bjt.md @@ -0,0 +1,39 @@ +# Bipolar Junction Transistor (BJT) + +## Purpose + +Bipolar Junction Transistors (BJTs) are versatile electronic devices that can act as amplifiers, switches, or in voltage regulation applications. BJTs come in two primary types: NPN and PNP, which refers to the arrangement of n-type and p-type semiconductor materials used in their construction. They are called "bipolar" because they operate with both electrons and holes as charge carriers. +Semiconductor Basics + +N-type Material: Doped with elements that have more electrons than silicon, providing extra free electrons as charge carriers. +P-type Material: Doped with elements that have fewer electrons than silicon, resulting in holes that act as positive charge carriers. + +## BJT Structure + +A BJT consists of three regions: the Emitter (E), Base (B), and Collector (C). In an NPN transistor, a thin p-type base is sandwiched between an n-type emitter and an n-type collector. For a PNP transistor, the material types for each region are reversed. + +## Operation Modes + +Active Mode: The emitter-base junction is forward-biased, and the collector-base junction is reverse-biased. In this mode, a small base current controls a much larger emitter-collector current. +Saturation Mode: Both the emitter-base and collector-base junctions are forward-biased. The BJT acts like a closed switch, allowing maximum current to flow from the collector to the emitter. +Cut-off Mode: Both the emitter-base and collector-base junctions are reverse-biased. The BJT acts as an open switch, with no current flowing through the collector-emitter path. + +## Current Flow + +In an NPN transistor, when the base-emitter junction is forward-biased, electrons move from the emitter into the base. Because the base is thin and lightly doped, most of these electrons cross through to the collector which is reverse-biased relative to the base. The movement of electrons constitutes the collector current (I_C), which is controlled by the base current (I_B). + +## Characteristics + +Beta (β or hFE): The current gain of a transistor in the active region, defined as the ratio of collector current to base current (β = I_C / I_B). +Alpha (α): Common-base current gain, defined as the ratio of collector current to emitter current (α = I_C / I_E). + +## Schematic Symbol + +An NPN BJT's schematic symbol consists of an arrow on the emitter pointing outwards, indicating conventional current flow from collector to emitter. The PNP symbol has the arrow pointing towards the base, indicating that the conventional current enters the emitter. + +## Pseudo Netlist + +A bjt has the following subckt definition: BJT Collector Base Emitter Transistor_Model + +In this simplified netlist structure, BJT denotes the transistor name, followed by the connection points for the collector, base, and emitter, respectively, and the last parameter is the specific model describing the BJT characteristics. + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/cdamplifier.md b/openfasoc/generators/glayout/glayout/llm/rag_data/cdamplifier.md new file mode 100644 index 000000000..8e7bdfb14 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/cdamplifier.md @@ -0,0 +1,42 @@ +# Common Drain Amplifier + +## Purpose + +The purpose of a common drain amplifier, aka a source follower, is to provide buffering to the input signal amplitude. It is typically used when impedance matching is required or to drive a low-impedance load, or to avoid loading down the previous stage of a circuit. + +It offers high input impedance, low output impedance, and a voltage gain close to unity. + +## Terms Defined + +Rs: the resistance of the biasing resistor. Can be interchange/replaced with another nmos' equivalent resistance
+Buffer: A circuit designed to isolate different stages of a circuit, often with unity gain, to prevent any loading effect.
+Unity Gain: A condition where the output signal is the same amplitude as the input signal (gain = 1).
+Input Impedance (Z_in): The impedance presented by the amplifier's input, which is typically high for a source follower.
+Output Impedance (Z_out): The impedance presented by the amplifier's output, which is typically low for a source follower.
+Transductance: Masure of amplification factor. Its measured by the ratio of the output current of the circuit over the output voltage of the circuit. + +## Theory + +The common drain amplifier works by applying an input signal to the gate of the nmos. The source of the nmos follows the input voltage. Because the drain is connected directly to the supply voltage, the device is in saturation. This allows the voltage gain to be at around unity. The high input impedance means minimal loading on the driving circuit, while the low output impedance allows the source follower to drive heavy loads effectively. + +## Schematic + +### In Words + +A typical source follower schematic includes: an nmos with the drain terminal connected to vdd and an input signal that is applied to the gate terminal. Additionally, there is a resistor (Rs) that connects the source terminal to the ground which sets the DC bias point and stabilizes the source voltage. The output node is at the source voltage of the transistor. + +If another nmos is used to replace the resistor, its source is connected to ground, the drain is connected to the source of the first transistor, and the gate is connected to a voltage called Vbias which controls the nmos' resistance. + +### Pseudo Netlist + +A nmos has the following subckt definition: NMOS drain gate source body + +A cd amp has the following subckt definition: .subckt NMOS Rs inputvoltage outputvoltage vdd gnd .endsubckt + +## Performance Specifications + +For an example source follower, typical performance specifications might include voltage gain, input impedance, output impedance, transconductance, and maximum output swing. + +The voltage gain (Av) can be defined as the ratio between output voltage over input voltage. This is equivalent to $(gm*Rs)/(1+gm*Rs)$. Additionally, the input impedance (Zin) is typically very high (infinite in an ideal circuit) because the gate of the nmos draws negligible current. The output impedance (Zout) is low and is determined by $Rs/(1+g_mRs)$ + +Bandwidth is the frequencies in which the amplifier works. This is generally wide in a cd amplifier since the gain is unity. The maximum output swing is the largest peak to peak voltage the circuit can output without distortion. In a source follower, the swing is vdd minuse the voltage drop over the transistor and resistor. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/classaamp.md b/openfasoc/generators/glayout/glayout/llm/rag_data/classaamp.md new file mode 100644 index 000000000..96ef65b6e --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/classaamp.md @@ -0,0 +1,37 @@ +# Class A Amplifier + +## Purpose + +The purpose of a Class A amplifier is to provide linear amplification of an input signal. This results in low distortion of the amplified signal. + +## Terms Defined +Biasing: Is where you set the operating point for your devices so they remain in the active region +Linearity: The proportion between the input and output signals where the output is a linear function of the input +Efficiency: The ratio of the power at the load over the total power consumed by the amplifier. +vgg: biasing voltage that is applied to m2, allows the transistor to act as a biasing resistor. + +## Theory + +Class A amplifiers typically has current flow in the mosfets during the entire period of a sinusiodal signal. It biases the transistor so any input signal causes the current flowing through to vary over the waveform. As a result, current is flowing through the output resulting in unsymmetrical sinking, poor efficiency, and a linear waveform. + +## Schematic + +### In Words + +A class a amplifier can be modeled with two nmos or two pmos transistors, m1 and m2 respectivly. The gate of m1 is connected to input voltage and the drain is connected the the drain of m2. The source of m1 is tied to vss and the source of m2 is connected to vdd. The gate of m2 is connected to vgg. The node between the drains of m1 and m2 are connected to vout which can be connect to a capacitor and resistor in parallel to block unwanted signals from reaching the load. + +### Pseudo Netlist + +A nmos has the following subckt definition: NMOS drain gate source body + +A class a amplifier has the following subckt definition: .subckt NMOS NMOS inputvoltage outputvoltage vss vgg vdd gnd Rl Cl .endsubckt + +## Performance Specifications + +For a class a amplifier, performance specifications may include efficiency, signal to noise ratio (SNR), linearity, and maximum output swing. + +Efficiency can be found by taking the output voltage at the peak over vdd-vss and squaring the result. For a class a amplifier, the circuit is very inefficient (usually between 20-30%) because the device is constantly active for an entire input signal cycle. + +The SNR of a class a amp is usually high since there is low distortion. Meanwhile, the class a amplifier is usually bery linear if its designed properly and in the linear operatin range. + +We typically get a high max output swing in terms of the supply voltage, the max output swing is the largest peak to peak voltage the circuit can output without distortion. diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/commondrain.md b/openfasoc/generators/glayout/glayout/llm/rag_data/commondrain.md new file mode 100644 index 000000000..71578ace5 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/commondrain.md @@ -0,0 +1,27 @@ +# Common Drain Stage +## Purpose +The common-drain stage, or source follower, serves as a voltage buffer in electronic circuit designs, providing a high input impedance, low output impedance, and voltage gain without phase inversion. + +## Terms Defined +Source follower - The gate of this transistor receives the input signal, the drain is connected to a power supply, and the source provides the output, which follows the input signal but subtracted down by the gate-source voltage drop, thus functioning as a buffer +Current source - In a simple configuration, this transistor operates in saturation and is biased such that it acts as a constant current sink, controlling the current flowing through M1. + +## Schematic +## Described in words +There are two MOSFETs. One acts like source follower which we will call M1, and the input is the gate of this transistor. The source of this transistor is connected to the drain of the second transistor and also serves as the output, and the drain is connected to a power supply. The second transistor is a current source, with the source tied to a lower voltage level from the power supply and the gate is biased to control the current sink. +### Pseudo Netlist +an nmos is has the following subckt definition: +NMOS drain gate source body + +a common drain stage has the following subckt definition: +.subckt commondrain vin vbias vdd vss vout +m1 vdd vin vout vss NMOS +m2 vout vbias vss vss NMOS +.endsubckt + +## Performance Specifications +M2 Biasing - M2 is biased to operate in its saturation region, where it behaves as a current source with a near-constant current, relatively independent of the drain-source voltage. +M1 as Source Follower - In this mode, the input voltage is applied to the gate of M1, and it operates in its saturation region due to the current supplied by M2. Since the gate to source voltage of M1 is relatively constant for a given operating point, the source voltage the output voltage follows the input voltage, hence the name "source follower." +Low Output Impedance - The output impedance of a source follower is low because the source tracks the gate voltage and the MOSFET acts to minimize any change in the source voltage by allowing more or less current to pass. This property makes the source follower an excellent buffer stage. +Voltage Gain - Ideally, the voltage gain of a source follower is slightly less than unity due to the voltage gate to source drop. This means that the output voltage almost equals the input voltage minus the gate-source voltage of M1. +High Input Impedance - Since the gate of a MOSFETs does not draw DC current, the input impedance of the source follower stage is very high, which is beneficial when interfacing with signal sources with high output impedances to prevent loading effects. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/commonsourceamp.md b/openfasoc/generators/glayout/glayout/llm/rag_data/commonsourceamp.md new file mode 100644 index 000000000..151865615 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/commonsourceamp.md @@ -0,0 +1,29 @@ +# Common Source Amplifier +## Purpose +The common source amplifier, serves as an amplifier with voltage gain and phase inversion. + +## Terms Defined +Amplifying Stage: The main amplifying transistor is in a common-source configuration. Its source is connected to a common reference (often ground), its gate receives the input signal, and the drain is connected to the active load. +Active Load - A second MOSFET, sometimes in a diode-connected configuration or part of a more complex current mirror, serves as the active load. This transistor is biased to operate as a constant current sink, creating a high output impedance at the drain of M1. + +## Schematic +## Described in words +There are two MOSFETs. One acts as the amplifying stage and we will call this M1, and the other is the active load which we will call M2. Depending on the type of active load, the port of the active load corresponding to the direction of current is connected to the drain of M1. The source of M1 is connected to a lower voltage level than the supply. +### Pseudo Netlist +an nmos is has the following subckt definition: +NMOS drain gate source body +a pmos is has the following subckt definition: +PMOS drain gate source body + +a common source amplifier with a biased pfet transistor has the following subckt definition: +.subckt commonsourceamp vin vbias vdd vss vout +m1 vout vin vss vss NMOS +m2 vout vbias vdd vdd PMOS +.endsubckt + +## Performance Specifications +Biasing - Both MOSFETs are biased in the saturation region. For M1, the gate-source voltage (VGS) is set above the threshold voltage (Vth) to ensure it remains in the saturation region across the input signal range. M2, configured as a current source, is also biased to ensure it stays in saturation. + +Input Signal - The input AC signal is superposed on the DC bias at the gate of M1. This causes variations in the gate-source voltage, modulating the drain current based on the transconductance (gm) of M1. + +Output Signal - The modulated drain current of M1 flows through the active load. Since the active load's output impedance is very high, usually much higher than a passive load resistor, the voltage swing at the drain of M1 is increased. The output signal is taken at this point. diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/csamplifier.md b/openfasoc/generators/glayout/glayout/llm/rag_data/csamplifier.md new file mode 100644 index 000000000..6c862811b --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/csamplifier.md @@ -0,0 +1,38 @@ +# Common Source Amplifier + +## Purpose +The purpose of a cs amp (common source amplifier) is to apply a small input voltage to a circuit to eventually obtain a large output voltage with respect to a common source that is tied to ground. + +## Terms Defined +Rl: the resistance of the load resistor in a cs amplifier. This resistance can be modeled by a transistor.
+Biasing: The process in which a circuit's transistor DC voltage and current is set to the operating range. + +## Theory + +The cs amplifier uses an nmos where the input signal is applied to the gate, the output is taken from the drain and the source is connected to a common reference. In this case the common reference is ground. The transistor is able to amplify the input signal by adjusting the resistance between the drain and source terminals. This adjusts the current through the load at the output which therefore amplifies the voltage. + +## Schematic + +### In Words + +A typical cs amp contains an nmos with these characteristics: its ground is tied to an an input voltage, the source is tied to ground, and the drain is connected to a load resistor (Rl) that is tied to ground. In betweeen Rl and the drain of the nmos is a node for the output voltage. + +In a circuit in which Rl is modeled by another transistor, the gate of the transistor above is tied to a node called Vbias. Its source is connected to vdd and its drain is connected to the other transistor. + +### Pseudo Netlist + +A nmos has the following subckt definition: NMOS drain gate source body + +A cs amp has the following subckt definition: .subckt NMOS Rl inputvoltage outputvoltage vdd gnd .endsubckt + +## Performance Specifications +The specifications of a cs amplifier are as follows: +Transductance(gm), Gain(Av), Zin, Zout, Bandwidth, and Cutoff Frequency. + +Transductance is a measure of amplification factor. Its measured by the ratio of the output current of the circuit over the output voltage of the circuit. + +Gain is the ratio of output voltage over input voltage. It can also be modeled by -gm*Rl (negative transductance multiplied by load resistance) + +Zin and Zout are the input impedance and the output impedance respectively. In a cs amplifier, the input impedance is equal to 0 and the output impedance is equal to the Rl (load resistance) of the circuit. + +Bandwith is the frequencies in which the amplifier works. Similarly, cutoff frequency is where the gains falls off 3dB from its mid band value. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/currentdivider.md b/openfasoc/generators/glayout/glayout/llm/rag_data/currentdivider.md new file mode 100644 index 000000000..e89c2778b --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/currentdivider.md @@ -0,0 +1,44 @@ +# Current Divider + +## Purpose + +A current divider is a circuit used to distribute an input current among multiple parallel branches according to their resistances. It is mainly utilized when one needs to have different current values in various parts of a circuit, such as in various sensors or transducers connected in parallel. It's a practical application of Ohm's law and Kirchhoff's current law (KCL). + +## Terms Defined + +I_in: The total input current entering the parallel network.
+I_out: The output current through a particular branch resistor.
+R_total: The equivalent resistance of the parallel network.
+Rn: Usually refers to an individual resistor within the set of parallel resistors.
+ +## Theory + +A current divider leverages Ohm's law and Kirchhoff's current law to split the currents in a parallel network. The current flowing through a resistor in parallel is inversely proportional to its resistance and directly proportional to all other resistances in the network. The formula for the current I_n through a specific branch resistor R_n is given by: + +$[ I_n = I_{in} \times \frac{R_{total}}{R_n} ]$ + +R_total is calculated as the reciprocal of the sum of the reciprocals of all resistances in the network: + +$[ \frac{1}{R_{total}} = \frac{1}{R_1} + \frac{1}{R_2} + \ldots + \frac{1}{R_n} ]$ + +## Schematic + +### Described in Words + +In a current divider circuit, resistors are arranged in parallel all sharing the same two nodes. The total current enters at the first node, flows through each parallel branch, and exits the second node, where the sum of the branch currents equates to the total current entering the network. + +### Pseudo Netlist + +A pseudo netlist of a current divider can be written as: +.subckt currentdivider In R1 R2 ... Rn R1 In Out VALUE_OF_R1 R2 In Out VALUE_OF_R2 Rn In Out VALUE_OF_Rn .endsubckt + +In this pseudo netlist, In is the node where the input current enters. R1...Rn are resistors with VALUE_OF_R1...VALUE_OF_Rn specified in ohms, and Out serves as the common connection point that combines the individual currents back together. + +## Performance Specifications + +Critical performance parameters for a current divider include: + +Current Sharing Accuracy: Defined by the tolerance of the resistors.
+Power Dissipation: Each resistor needs to have a sufficient power rating to safely dissipate the heat generated by the current passing through it.
+Temperature Stability: Resistance changes with temperature, which affects current division, so resistors with low temperature coefficients are preferable for higher precision. + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/currentmirror.md b/openfasoc/generators/glayout/glayout/llm/rag_data/currentmirror.md new file mode 100644 index 000000000..184f54275 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/currentmirror.md @@ -0,0 +1,23 @@ +# Current Mirror +## Purpose +A current mirror is a circuit designed to copy a current through one active device by controlling the current in another active device of a circuit, keeping the output current constant regardless of loading. + +## terms defined +reference: reference transistor. +mirror: the transistor which mirrors reference current +ratio: width ratio between mirror and reference, used to tune the relative current between mirror drain and reference drain +## schematic +### described in words +two transistors (either nfet or pfet) one labeled as the reference which accepts an input current at the drain, and one labeled as mirror which has the output current at the drain. The sources of reference and mirror are connected and the gates of reference and mirror are also connected. The drain of the reference is connected to gate of reference. +### Pseudo Netlist +an nmos is has the following subckt definition: +NMOS drain gate source body + +the nfet or n-type current mirror has the following subckt definition: +.subckt currentmirror inputcurrent outputcurrent +reference inputcurrent inputcurrent gnd gnd NMOS +mirror outputcurrent inputcurrent gnd gnd NMOS +.endsubckt + +## Performance Specifications +There are three main specifications that characterize a current mirror. The first is the transfer ratio (in the case of a current amplifier) or the output current magnitude (in the case of a constant current source CCS). The second is its AC output resistance, which determines how much the output current varies with the voltage applied to the mirror. The third specification is the minimum voltage drop across the output part of the mirror necessary to make it work properly. This minimum voltage is dictated by the need to keep the output transistor of the mirror in active mode. The range of voltages where the mirror works is called the compliance range and the voltage marking the boundary between good and bad behavior is called the compliance voltage. There are also a number of secondary performance issues with mirrors, for example, temperature stability. diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/diffpair.md b/openfasoc/generators/glayout/glayout/llm/rag_data/diffpair.md new file mode 100644 index 000000000..cedb61f0b --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/diffpair.md @@ -0,0 +1,32 @@ +# Differential Pair +## Purpose +The gates of the MOSFETs that make up the differential pair act as inputs. If there is a difference in the voltage at the gates of the NMOS' then the transistor with the higher gate voltage will conduct more current leading to a difference in the currents at the drains of the transistors. + +## Terms Defined +Differential Mode - When a differential voltage is applied to the gates of M1 and M2, the MOSFET with the higher gate-source voltage will enter deeper into the saturation region, conducting more current. Consequently, the other MOSFET will conduct less current. This results in an imbalance in drain currents, creating a voltage differential at the output proportional to the input signal difference. + +Common-Mode Operation - If an identical voltage, referred to as the common-mode voltage, is applied to both gate terminals, ideally, there is no change in the drain current balance since no differential signal is present. MOSFETs in a well-matched differential pair will each conduct half the total current of the current source at the source of the NMOS', assuming they are operating above the threshold voltage and in saturation. + +## Schematic +## Described in words +There are two MOSFETs, with the sources shorted and connected to a current source. The outputs are the drains of the MOSFETs. +### Pseudo Netlist +an nmos is has the following subckt definition: +NMOS drain gate source body + +the nfet or n-type differential pair has the following subckt definition: +.subckt diffpair in1 in2 out1 out2 ibias +m1 out1 in1 ibias gnd NMOS +m2 out2 in2 ibias gnd NMOS +.endsubckt + +## Performance Specifications +Transconductance (g_m): For MOSFETs, transconductance is the ratio of the change in the drain current (ΔI_D) to the change in the gate-source voltage (ΔV_GS) when the drain-source voltage (V_DS) is constant. g_m quantifies the MOSFET's ability to convert a voltage input (at the gate) into a current output (at the drain). It is essential in determining the differential gain of the pair. + +Differential Gain (A_diff): The differential gain is a function of the transconductance of the MOSFETs and the total load resistance (R_D) seen by each transistor's drain. In a MOSFET differential pair, A_diff is typically given as A_diff = g_m * R_D. + +Common-Mode Gain (A_cm): In a practical MOSFET differential pair, the gain for common-mode signals is not perfectly zero due to device mismatches and non-idealities. The goal is to minimize A_cm through careful circuit design and component matching. + +Common-Mode Rejection Ratio (CMRR): The CMRR measures how well the differential pair rejects common-mode signals and amplifies differential signals. It is given as CMRR = A_diff / A_cm, and like in BJTs, it is an important metric that is ideally large. + +Input Range: The voltage range over which the differential pair operates linearly is limited by the need to keep both MOSFETs in saturation and the matching of the pairs. Large differential input signals may push one of the MOSFETs into the triode region or cutoff, leading to nonlinear operation. diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/diode.md b/openfasoc/generators/glayout/glayout/llm/rag_data/diode.md new file mode 100644 index 000000000..6c9f5d517 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/diode.md @@ -0,0 +1,47 @@ +# Diode + +## Purpose + +A diode is a two-terminal semiconductor device that allows current to flow primarily in one direction (forward direction), showing a low resistance, while offering high resistance to current in the opposite (reverse) direction. Diodes are fundamental components used in various electronic circuits for rectification, signal modulation, voltage regulation, switching, signal mixing, and many other applications. + +## Terms Defined + +Anode: The terminal through which conventional current flows into the diode, typically marked with a plus sign. +Cathode: The terminal through which conventional current flows out of the diode, often denoted with a line or band on the diode body. +Forward Bias: A condition where the anode is more positive relative to the cathode, allowing current flow. +Reverse Bias: A condition where the cathode is more positive in relation to the anode, restricting current flow. +Forward Voltage Drop (Vf): The potential difference across the diode terminals when current is conducted in the forward direction, typically 0.7V for silicon diodes and 0.3V for germanium diodes. +Reverse Breakdown Voltage (V_br): The voltage at which the diode will conduct a significant reverse current, potentially leading to device damage if sustained. + +## Theory + +A semiconductor diode is an electronic component that conducts current primarily in one direction. It consists of a p-n junction, which forms when p-type and n-type semiconductor materials are joined together. When a diode is forward biased (anode voltage is higher than cathode voltage), the junction's depletion zone narrows, allowing current to flow easily due to a reduction in the barrier potential. The forward current increases exponentially with the voltage applied across the diode, as defined by the Shockley diode equation. In contrast, when the diode is reverse biased (cathode voltage is higher than anode voltage), the depletion zone and barrier potential grow, inhibiting current flow except for a tiny leakage current due to minority carriers. If the reverse bias voltage exceeds a critical threshold, known as the breakdown voltage, the diode will start conducing in reverse, which can be destructive unless the current is limited. Diodes are thus key in directing current in circuits and are used for various purposes, including rectification, regulation, and protection against voltage spikes. + +## Schematic + +### In Words + +A diode symbol in a schematic circuit diagram is represented by a triangle pointing towards a line. The triangle symbolizes the direction of the allowable current flow (pointing from anode to cathode), and the line symbolizes the barrier that prevents current in the reverse direction. + +The anode is connected to the end of the triangle. +The cathode is connected to the line. + + +### Pseudo Netlist + +.subckt diode anode cathode D1 anode cathode DIODE_MODEL .endsubckt + +DIODE_MODEL represents the specific parameters or model of the diode, which might be defined elsewhere in the netlist file with characteristics like saturation current, breakdown voltage, and more. + +## Performance Specifications + +The performance of a diode can be defined by several characteristics: + +Maximum Forward Current (If_max): The maximum allowable current the diode can conduct in the forward direction without overheating or damage.
+Maximum Reverse Voltage (Vr_max): The maximum voltage the diode can withstand in the reverse direction before breakdown occurs.
+Forward Voltage Drop (Vf): Voltage required across the diode to start conducting substantially.
+Reverse Leakage Current (Ir): The small current that flows through the diode when reverse biased.
+Reverse Recovery Time (trr): The time it takes for the diode to stop conducting after switching from forward to reverse bias.
+Junction Capacitance (Cj): An intrinsic property that affects the diode's response to AC signals, higher at lower reverse biases. + +Diodes come in many types to serve specific functions such as general-purpose rectification (rectifier diodes), signal detection (small-signal diodes), voltage clamping (zener diodes), fast switching (Schottky diodes), and light emission (LEDs). \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/hpf.md b/openfasoc/generators/glayout/glayout/llm/rag_data/hpf.md new file mode 100644 index 000000000..eceb42b98 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/hpf.md @@ -0,0 +1,41 @@ +# High Pass Filter + +## Purpose + +A high pass filter is an electronic circuit that allows signals with a frequency higher than a certain cutoff frequency to pass through and attenuates frequencies lower than the cutoff frequency. It is used in audio applications to block low-frequency noise, in communication systems to eliminate interference, and in various signal processing tasks that require isolation of the high-frequency content of a signal. + +## Terms Defined + +Cutoff Frequency (f_c): The frequency at which the output signal power falls to half the power of the input signal (corresponds to -3dB point in magnitude). +Passband: The range of frequencies that the filter allows to pass with little or no attenuation. +Stopband: The range of frequencies that are significantly attenuated by the filter. +Roll-off Rate: The rate at which the filter attenuates frequencies beyond the cutoff frequency, often expressed in dB per octave or dB per decade. +Reactance: The resistance to the change of current by a capacitor, inversely proportional to the frequency. + + +## Theory + +## Schematic + +### In Words + +In its simplest form, a high pass filter can be constructed with a resistor (R) and a capacitor (C) in series. In a passive RC high pass filter: + +The input signal is fed through the capacitor.
+The resistor is connected to the capacitor and then grounded.
+The output signal is taken across the resistor. + +### Pseudo Netlist + +A high pass filter has the following subckt definition: .subckt inputvoltage R C gnd outputvoltage .endsubckt + +## Performance Specifications + +For a high pass filter, important performance characteristics include: + +Cutoff Frequency (f_c): Calculated as $( f_c = \frac{1}{2\pi RC} )$, where R is resistance and C is capacitance. +Bandwidth: The width of the passband, which, for a high pass filter, extends from the cutoff frequency to the highest frequency unattenuated by the filter. +Roll-off Rate: For an ideal RC high pass filter, it is 20 dB/decade or 6 dB/octave, meaning for every tenfold increase in frequency past the cutoff, the response decreases by 20 dB. +Impedance Matching: The filter's input and output impedance characteristics should match the source and load impedance to prevent signal reflections and power loss. + +In addition to these passive RC filters, high pass filters can also be active, including amplifying components like transistors or operational amplifiers, which can provide gain and change the dynamics of the cutoff frequency and roll-off. Active high pass filters are especially useful when the signal needs amplification or a more precise filter response is required. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/inverter.md b/openfasoc/generators/glayout/glayout/llm/rag_data/inverter.md new file mode 100644 index 000000000..dbab455b6 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/inverter.md @@ -0,0 +1,35 @@ +# Static CMOS Inverter + +## Purpose +An inverter is used to implement the logic function NOT by inverting the input voltage from high to low or from low to high. + +## Terms Defined +VTC: voltage transfer characteristic, a graph that describes the relationship between voltage and time based on certain inputs.
Sizing Ratio: the ratio between the pmos and the nmos. used to tune the speed and strength of the various components of the inverter + +## Theory +An inverter is a simple logic circuit that can be implemented by two transistors: an nmos and pmos. This method of implementing an inverter is called a static cmos logic inverter. + +The pmos is typically placed above the nmos since the pmos is responsible for pulling up while the nmos is responsible for pulling down. When a high input voltage is given to the inverter, the nmos is switched ON and pulls the circuit down. Meanwhile, when a low input voltage is given, the pmos is switched OFF and pulls the circuit up. + +An ideal inverter has a VTC curve that switches from high to low voltage or low to high voltage instantaneously. An advantage of the static cmos inverter is that a pmos is very good for pulling up and an nmos is good for pulling down, making a Voltage Transfer Characteristic that is very sharp. + +## Schematic + +### In Words +An inverter has an nmos and a pmos. The pmos is above the nmos and it's source is tied to Vdd. The nmos' source is tied to ground. The drains for the nmos and pmos are connected together. The output voltage can be measured at this node that connects the nmos and pmos. Additionally, the nmos and pmos are tied together at its gate as well. This is where the input voltage is measured. The sizing ratio between the pmos and nmos is typically the most optimal when it is 2:1. + +### Pseudo Netlist +A nmos has the following subckt definition: NMOS drain gate source body + +A pmos has the following subckt definition: PMOS drain gate source body + +A CMOS Inverter has the following subckt definition: .subckt inverter inputvoltage outputvoltage NMOS PMOS gnd vdd .endsubckt + +## Performance Specifications +The specifications that define an inverter are as follows: Propagation Delay (tpd), Power Dissipation and Noise Margins. + +Propagation delay is the average time it takes for the output to switch states when the input is changed. It is generally defined between the 50% points of voltage levels during a transition. Propagation delay can be controlled by the sizing ratio + +Power Dissipation is a negative side effect of that inverters have. Its typically caused in two instances, static and dynamic. Dynamic power dissipation occurs when switching occurs an a short circuit briefly happens. Static power dissipation happens when the circuit is in steady state. + +Noise margins are the amount of noise voltage that can be fed into the input voltage without affecting the output. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/lpf.md b/openfasoc/generators/glayout/glayout/llm/rag_data/lpf.md new file mode 100644 index 000000000..02f25da83 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/lpf.md @@ -0,0 +1,35 @@ +# Low Pass Filter + +## Purpose +A low pass filter is a circuit made to allow signalls with a frequency lowed than a specified cutoff frequency to pass through while cutting off frequencies that are higher. + +## Terms Defined +Cuttoff Frequency ($f_c$): The frequency in which the low pass filter will begin to filter signals. + +Passband: The range of frequencies that are allowed to pass through the filter. + +Stopband: The range of frequencies that are not alloed to pass through. + +Attenuation Rate: The rate where the signal is reduced past the cutoff frequency. Measured in dB/octave or dB/decade + +## Theory +A simple low pass filter can be composed of a single resistor and capacitor (RC). Since the capacitor's impedance decreases when frequency increases, there is greater attenuation at high frequencies. More complex filters can be implemented by uing more stages of RC. + +## Schematic + +### In Words +A simple low pass filter will contain an input resistor connected in series with the input voltage. The capacitor will be connected in parallel with the output voltage and be tied to ground. And the output voltage is taken across the capacitor. + +### Pseudo Netlist + +A low pass filter has the following subckt definition: .subckt inputvoltage R C gnd outputvoltage .endsubckt + +## Performance Specifications + +For a simple low pass filter, typical performance specifications may include cutoff frequency, passband, and phase shift. + +The cutoff frequency is the frequency at which the amplitude of the output signal is reduced by -3 dB from the input and is calculated as $1/(2πRC)$. Based off this, the passband ranges from DC to slightly below the cutoff frequency. + +A phase shift typically occurs in a lpf. At the cutoff frequency, the filter has a phase shift of -45 degrees. At other frequencyes, the filter can have a phase shift that ranges from 0 at DC to -90 at very high frequencies. + + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/mosfet.md b/openfasoc/generators/glayout/glayout/llm/rag_data/mosfet.md new file mode 100644 index 000000000..000f65391 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/mosfet.md @@ -0,0 +1,47 @@ +# MOSFET Structure and Basic Operation + +A MOSFET includes four terminals: Source (S), Gate (G), Drain (D), and Body (B). However, in many circuits, the body terminal is internally connected to the source. The flow of charge carriers (either holes or electrons) through the channel between the source and drain is controlled by the voltage applied to the gate. + +## n-channel MOSFET (nFET or NMOS) + +Charge Carriers: Electrons (which have higher mobility than holes). +Substrate Type: Typically made with a p-type substrate. +Threshold Voltage (Vth): Positive. For the transistor to conduct, the gate-to-source voltage (Vgs) must be higher than Vth. +On-state Resistance: Lower than that of a PMOS of similar size because electrons have higher mobility than holes. This property makes NMOS transistors more efficient as switches. +Symbol: The arrow on the source terminal points out, indicating electron flow out of the device (since conventional current direction is opposite to electron flow). + + ## p-channel MOSFET (pFET or PMOS) + +Charge Carriers: Holes. +Substrate Type: Typically made with an n-type substrate. +Threshold Voltage (Vth): Negative. To turn on the PMOS, Vgs must be lower (more negative) than Vth. +On-state Resistance: Higher than that of an NMOS due to the lower mobility of holes. +Symbol: The arrow on the source terminal points in towards the device, indicating hole flow into the device (conventional current flows from source to drain). + + +Polarity: NMOS transistors turn on (conduct) when the gate voltage is positive with respect to the source. PMOS transistors turn on when the gate voltage is negative with respect to the source. +Conductivity and Efficiency: NMOS transistors generally conduct better due to the higher mobility of electrons, which makes them preferred when the design requires lower power consumption and higher efficiency. +Threshold Voltage: NMOS devices typically have a positive threshold voltage, while PMOS devices have a negative threshold voltage. +Source-Drain Configuration: In circuits where both NMOS and PMOS are used, the PMOS is more often connected to the positive supply voltage and serves as a load or pull-up, while the NMOS is connected to the ground and serves as a driver or pull-down. +Complementary Use: NMOS and PMOS transistors are often paired in CMOS (Complementary MOS) technology, which is used in virtually all modern integrated circuits, including microprocessors and memory chips. + + +## Regions of Operation +Cutoff Region (Subthreshold Region): When the gate-source voltage (Vgs) is below the threshold voltage (Vth), the transistor is turned off; there is no conductive channel and therefore no current flows from drain to source (Ids ≈ 0). + +Triode Region (Ohmic Region or Linear Region): When Vgs is greater than Vth, and the drain-source voltage (Vds) is low, the MOSFET operates in the triode region. Here, the channel is formed, and Ids increases with Vds. The MOSFET behaves like a variable resistor in this region. + +Saturation Region (Active Region): If Vds is increased further and exceeds (Vgs - Vth), the MOSFET enters saturation. In this region, the current Ids becomes relatively independent of Vds and is primarily a function of Vgs. For short-channel devices with significant channel length modulation, a more complete model would include the effect of Vds on Ids even in the saturation region. + +## Current Sources and Current Sinks + +MOSFETs are frequently used to create current sources and sinks, which provide constant current regardless of the load or supply voltage. +Current Source: A current source delivers a nearly constant current to a circuit. It can be made with a MOSFET by setting it to operate in the saturation region. When designed properly, the MOSFET's Ids is mostly determined by Vgs and negligibly affected by Vds. This stability makes the MOSFET act as a reliable current source. Feedback mechanisms or advanced biasing techniques (like using a constant-current diode) may be employed to enhance the current stability against variations in temperature and supply voltage. +Current Sink: A current sink is analogous to a current source but sinks a constant current from a circuit, pulling it towards a lower potential (usually ground). The operation and design principles are similar to those of a current source, but now the current is flowing into the transistor instead of out of it. + +In both cases, the MOSFET's gate voltage is usually set through a voltage divider or another biasing circuit. Sometimes, current mirrors are used, where one transistor is used to set the operating point, and another transistor (matched to the first) mirrors its current. +Design Considerations + +Biasing: Proper DC biasing is critical so the device operates in the desired region under all expected conditions. +Temperature Dependence: Semiconductor characteristics, including threshold voltage and mobility, depend on temperature, possibly affecting the MOSFET's operation. +Process Variation: Slight variations from the manufacturing process can lead to different thresholds and gains, requiring circuit design to account for variability. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/mux.md b/openfasoc/generators/glayout/glayout/llm/rag_data/mux.md new file mode 100644 index 000000000..c3a40bd08 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/mux.md @@ -0,0 +1,50 @@ +# Multiplexer (MUX) +## Purpose + +A multiplexer, commonly known as a MUX, is a combinational logic circuit designed to select binary data from one of many input lines and direct it to a single output line. The selection of the input is controlled by additional inputs known as select lines. MUXes are widely used in digital applications like information routing, data compression, and resource management, allowing multiple signals to share a single resource. +Terms Defined + +Input Lines: Multiple binary data inputs that are candidates for selection by the MUX.
+Output Line: The single binary data line that carries the selected input to the next stage of the circuit.
+Select Lines: Binary inputs that control which of the data input lines is connected to the output.
+Truth Table: A tabulation that specifies the output of the MUX for each possible combination of select line values. + +## Schematic +### Described in Words + +The schematic for a generic MUX with n select lines and 2^n input lines typically includes: + +Data Inputs: Labeled as D0, D1, D2, ..., D(2^n - 1), where each D represents one possible data input. +Select Inputs: Labeled as S0, S1, ..., S(n-1), where each S represents one bit in the binary selection code. +Output: A single line where the selected data input will appear. + +In terms of internal logic, the MUX may use a series of gates such as AND, OR, and NOT to control which input is passed to the output based on the select lines. Specifically: + +Each data input Di is fed into an AND gate. +A set of NOT gates generate the complement of each select line. A decoding logic network uses the select lines and their complements to activate only one AND gate corresponding to the selected input. The outputs of all these AND gates are then combined in a single OR gate, whose output becomes the MUX output. + +### Pseudo Netlist + +A nmos has the following subckt definition: nMOS drain gate source body + +A pmos has the following subckt definition: pMOS drain gate source body + +A two to one mux has the following subckt definition:.subckt twotoonemux D0 D1 S Y +TG0 D0 Y notS gnd vdd nMOS pMOS +TG1 D1 Y S gnd vdd nMOS pMOS +.endsubckt + +TG0 and TG1 are transmission gates with D0 and D1 as inputs and Y as the muxed output. S is the select signal and notS is its inverse. The nMOS and pMOS are placeholders for the n-type and p-type MOS transistors. + +## Performance Specifications + +The performance of a multiplexer is characterized by several factors: + +Propagation Delay (t_pd): The time taken for a change at an input or select line to result in a change in the output.
+Power Consumption: The total power used by the MUX during operation, which can vary depending on the logic family and technology used.
+Channel On Resistance: For analog MUXes, this is the resistance seen by a signal passing through the MUX.
+Off Isolation: The measure of the MUX's ability to prevent signal leakage from unselected inputs to the output.
+Crosstalk: Unwanted coupling between the MUX's inputs which can lead to signal integrity issues.
+Bandwidth: For analog MUXes, the range of frequencies the MUX can pass without significant attenuation or distortion. + +The efficient design of multiplexers is crucial for high-performance digital systems where signal fidelity and switching speed are of paramount importance, like in communication systems, data processors, and computer memory management. diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/opamp.md b/openfasoc/generators/glayout/glayout/llm/rag_data/opamp.md new file mode 100644 index 000000000..845fe61d5 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/opamp.md @@ -0,0 +1,52 @@ +# Opamp + +## Purpose + +An operational amplifier, commonly known as an op amp, is a voltage amplifying device designed to be used with external feedback components, such as resistors and capacitors, between its output and input terminals. These components determine the operation or "amplification function" of the amplifier, which can range from simple amplification to complex filtering, mathematical operations, signal conditioning, and more. + +## Terms Defined + +Inverting Input (IN-): The input terminal where the signal is inversely amplified.
+Non-inverting Input (IN+): The input terminal where the signal is amplified retaining its original phase.
+Output Terminal: Where the amplified signal is provided.
+Power Supply Ports: Two ports, one for the positive power supply voltage (V+), and one for the negative (V-).
+Bias Current: The current that flows into the input terminals due to internal transistor biasing.
+Open-Loop Gain: The amplification factor of the operational amplifier without any feedback.
+Slew Rate: The maximum rate at which the output voltage can change, often specified in V/µs.
+Offset Voltage: A small voltage that must be applied between the input terminals to ensure a zero volt output without any signal. + + +## Theory + +An ideal operational amplifier is a theoretical construct that serves as a standard against which real-world op amp performance is measured. The characteristics of an ideal op amp include: + +1. Infinite open-loop gain (A_vo), which means an infinite amplification factor without feedback.
+2. Infinite input impedance (Z_in), so that it draws no current from the source.
+3. Zero output impedance (Z_out), enabling it to provide unlimited current to the load.
+4. Infinite bandwidth with no phase shift and flat frequency response from DC to the highest AC frequencies.
+5. Zero offset voltage at the input, ensuring a zero-output voltage when both inputs are at the same voltage.
+6. Infinite common-mode rejection ratio (CMRR) and power supply rejection ratio (PSRR), implying no sensitivity to voltages that appear at both inputs or to fluctuations in supply voltage. + +These ideal characteristics allow for simplified analysis and design, understanding that real-world op amps will fall short of these ideals. + +## Schematic + +### In Words + +The op amp symbol consists of a triangle pointing to the right with five terminals: two input terminals on the left side, one output terminal on the triangle's right tip, and two power supply terminals (-V and +V) at the top and bottom respectively. The non-inverting input (IN+) is usually shown at the top left of the triangle, and the inverting input (IN-) is depicted below it. + +### Pseudo Netlist + +An operational amplifier has the following subckt definition: .subckt opamp inverting_input non_inverting_input output v_positive v_negative + +## Performance Specifications + +Key specifications for op amps include:
+Input Impedance: Ideally infinite to ensure no current flows into the inputs.
+Output Impedance: Ideally zero, representing a perfect voltage source.
+Gain Bandwidth Product (GBP): The product of the op amp’s bandwidth and the gain at which it can operate.
+Common-Mode Rejection Ratio (CMRR): The ability of the op amp to reject common-mode signals applied to both inputs.
+Total Harmonic Distortion (THD): A measure of the distortion added to the input signal.
+Supply Rejection Ratio (PSRR): The ability of an op amp to reject fluctuations in its supply voltage.
+Temperature Coefficient: Describes how the op amp parameters change with temperature. + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/pushpullamp.md b/openfasoc/generators/glayout/glayout/llm/rag_data/pushpullamp.md new file mode 100644 index 000000000..416b8cb24 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/pushpullamp.md @@ -0,0 +1,44 @@ +# Push Pull Amp (Class B Amplifier) + +## Purpose + +A push-pull amplifier is an amplifier that uses a pair of active devices (transistors or tubes) that alternately amplify the positive and negative halves of an input signal waveform. This configuration is widely used because it improves efficiency by reducing the power wasted in the form of heat when compared to single-ended amplifier designs. + +## Terms Defined + +Class: The amplifier class, such as Class A, B, AB, or C, indicating the portion of the input signal cycle during which each transistor conducts. +Active Device: The amplifying element, often a transistor, that can control a large current flow with a smaller input signal. +Biasing: The process of setting the initial operating point of an active device. +Crossover Distortion: A form of distortion that occurs in Class B and AB push-pull amplifiers due to the transition between the "push" and "pull" transistors. + +## Theory + +The push-pull design uses two active devices to amplify different halves of the input waveform. In a Class B amplifier, one device amplifies the positive half-cycle while the other device handles the negative half-cycle. A Class AB design utilizes a small bias to both devices to ensure they are both conducting in the small region around the zero-crossing of the signal, thus reducing crossover distortion associated with Class B. + +## Schematic + +### In Words + +A push-pull amplifier typically consists of two transistors, an NPN (or N-channel MOSFET) and a PNP (or P-channel MOSFET) transistor. The emitters (or sources) of the two transistors are connected together and to the output load. The bases (or gates) receive the input signal through a phase splitter, which creates two signals that are 180 degrees out of phase with each other. The collectors (or drains) of each transistor are connected to the supply voltages. + +### Pseudo Netlist + +A push pull amp has the following subckt definition: .subckt pushpullamp input output vcc vee +Q1 NPN_collector input common vcc NPN +Q2 PNP_collector input common vee PNP +.endsubckt + +## Performance Specifications + +For push-pull amplifiers, the performance specifications typically include: + +Efficiency: Refers to the percentage of input power that is converted to useful output power as opposed to being dissipated as heat.
+Linearity: The ability to amplify all parts of the input signal equally, which determines the fidelity of the amplification.
+Power Output: The maximum signal power that the amplifier can deliver to the load.
+Gain: The ratio of the output signal amplitude to the input signal amplitude.
+Output Impedance: The impedance that the amplifier presents to the load, which affects the power transfer.
+Frequency Response: The range of frequencies over which the amplifier operates effectively.
+Harmonic Distortion: The generation of overtones that were not present in the input signal, typically more noticeable at higher power levels. + +Real-world push-pull amplifiers often incorporate additional components and circuitry for biasing, phase splitting, and feedback to overcome non-idealities such as crossover distortion and to improve performance metrics like linearity and frequency response. + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/resistor.md b/openfasoc/generators/glayout/glayout/llm/rag_data/resistor.md new file mode 100644 index 000000000..93fd47720 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/resistor.md @@ -0,0 +1,28 @@ +# Resistor + +## Purpose + +A resistor is a passive electrical component that provides electrical resistance in an electronic circuit. The main purpose of a resistor is to limit electrical current, divide voltages, and dissipate power as heat. They are essential in creating specific voltage or current relationships in circuits, adjusting signal levels, biasing active elements, and terminating transmission lines, among many other applications. + +## Ohm's Law + +The fundamental characteristic of a resistor is described by Ohm's Law, which states that the current (I) through a conductor between two points is directly proportional to the voltage (V) across the two points and inversely proportional to the resistance (R) of the conductor: + +$[ V = I \times R ]$ + +## Construction and Characteristics + +Resistors can be constructed from various materials, such as carbon, metal film, or wires wound around a core. They are specified by resistance value, power rating, tolerance, temperature coefficient, and stability. The resistance value determines how much it will oppose current flow, while the power rating specifies the maximum power it can dissipate without damage. +Schematic Symbol + +The schematic symbol for a resistor is typically a zigzag line with two protruding terminals representing the points of electrical connection. European schematics sometimes use a simple rectangle instead of the zigzag symbol. + + +## Performance Specifications + +Resistance: The main parameter of a resistor, typically measured in ohms (Ω).
+Tolerance: Indicates how much the actual resistance can vary from the specified value, often given as a percentage (e.g., ±1%, ±5%).
+Power Rating: The maximum power, usually in watts (W), that a resistor can safely dissipate as heat.
+Temperature Coefficient: Describes how the resistance changes with temperature, important for precision applications. + +Resistors are reliable, economical, and widely available components that remain integral to nearly every electronic circuit. Whether used singly or in networks, resistors shape the behavior of circuits in predictable and controllable ways. diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/ringoscillator.md b/openfasoc/generators/glayout/glayout/llm/rag_data/ringoscillator.md new file mode 100644 index 000000000..4159db158 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/ringoscillator.md @@ -0,0 +1,41 @@ +# Ring Oscillator + +## Purpose + +A ring oscillator is a type of electronic oscillator that produces a periodic oscillating electronic signal (often a square wave). It consists of an odd number of NOT gates or inverters whose output is connected back to the input, forming a closed loop. The main purpose of the ring oscillator is to generate a high-frequency clock signal and for use in integrated circuits as a timing element. It's also frequently used in characterizing and testing the properties of semiconductor technologies, such as propagation delay and signal integrity. + +## Terms Defined + +Inverter: A logic gate that outputs the complement (opposite) of its input signal.
+Propagation Delay (t_pd): The time delay it takes for a signal to pass through one stage of the circuit, such as an inverter.
+Frequency (f): The frequency of oscillation, determined by the number of inverters and the propagation delay. + +## Theory + +The operation of a ring oscillator is based on the propagation delay inherent in the inverters. When the loop is closed, an initial change in voltage at the first inverter's input propagates through each inverter in the chain. Since an odd number of inverters are used, the output of the last inverter is an inverted version of the original signal, which becomes the input to the first inverter, perpetuating the oscillation. The frequency of oscillation is inversely proportional to the total propagation delay through the loop (the sum of the delay through each inverter and any delay from wiring) and can be estimated by: + +$[ f = \frac{1}{t_{pd} \times N} ]$ + +where $( t_{pd} )$ is the individual delay for one inverter and ( N ) is the number of inverters. + +## Schematic + +### Described in Words + +A ring oscillator circuit uses an odd number of inverters connected in series. The output of the last inverter is fed back to the input of the first, creating a feedback loop. + +### Pseudo Netlist + +A pseudo netlist of a ring oscillator can be written as:.subckt ringoscillator out X1 out net1 INV X2 net1 net2 INV ... XN net(N-1) out INV .endsubckt + +In this netlist, X1...XN denote individual inverters, net1...net(N-1) represent internal connections between inverters, and INV stands for the inverter model. + +## Performance Specifications + +Performance characteristics of a ring oscillator include: + +Frequency Stability: Often limited due to sensitivity to supply voltage variations and temperature changes.
+Power Consumption: Related to the number of stages and the frequency of operation, with higher frequencies generally leading to higher power usage.
+Phase Noise: The short-term frequency stability of the oscillator, affected by the quality of the inverters and the supply noise. + + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/tia.md b/openfasoc/generators/glayout/glayout/llm/rag_data/tia.md new file mode 100644 index 000000000..bea41570b --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/tia.md @@ -0,0 +1,40 @@ +# Transimpedance Amplifier (TIA) + +## Purpose + +A Transimpedance Amplifier (TIA) is an amplifier that converts an input current to a proportional output voltage. The primary role of a TIA is in applications where current signals need to be detected or measured, and amplified into a more usable voltage level. Commonly, TIAs are used with photodiodes, photomultiplier tubes, or other sensors that produce a current proportional to the intensity of light they receive. + +## Terms Defined + +Photodiode: A semiconductor device that generates a current when exposed to light.
+Feedback Resistor $(R_f)$: The resistor in the feedback loop of the TIA that sets the gain of the amplifier.
+Transimpedance Gain: The ratio of the output voltage to the input current, given in ohms, which is numerically equal to the value of the feedback resistor (R_f). + +## Theory + +The TIA uses a feedback resistor to set the gain of the amplifier and convert a small input current (I_in) from a sensor or photodiode to a larger output voltage (V_out). Ohm’s law describes the relationship between the input current and the output voltage of a TIA: $V_out = I_in * R_f$. Because the input is current and the output is voltage, the transimpedance gain has units of resistance, which is what gives the TIA its name. In an ideal TIA, the input impedance is close to zero, which ensures that the current flows through the amplifier, and the output impedance is low. + +## Schematic + +### Described in Words + +In the schematic of a TIA, an op-amp is configured with the photodiode connected between its inverting input and the ground, effectively reverse-biasing the photodiode when the op-amp output swings positive. The non-inverting input is connected to a reference voltage, often ground. A feedback resistor is connected between the op-amp's output and its inverting input. + +### Pseudo Netlist + +An operational amplifier has the following subckt definition: .subckt opamp inverting_input non_inverting_input output v_positive v_negative + +A pseudo netlist of a tia can be written as: +.subckt tia pd_in v_out v_ref XU1 op_out pd_in v_ref opamp Rf op_out pd_in .endsubckt + +In this netlist, XU1 represents the operational amplifier, pd_in is the input from the photodiode, v_out is the output voltage of the TIA, and v_ref is the reference voltage for the non-inverting input. Rf is the feedback resistor which sets the gain of the TIA. + +## Performance Specifications + +Important specifications for a TIA include: + +Bandwidth: The range of frequencies over which the TIA will have a consistent gain. This is often affected by the feedback resistor and any parasitic capacitance at the input.
+Noise Performance: Critical for the sensitivity of the TIA as it amplifies the signal from the photodiode. Includes contributions from the feedback resistor, the op-amp, and the diode itself.
+Dynamic Range: The range of input currents over which the TIA can provide a linear output voltage response.
+Stability: Factors such as the capacitance of the photodiode and the value of Rf can affect the stability and may lead to oscillations if not properly compensated. + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/transmissiongate.md b/openfasoc/generators/glayout/glayout/llm/rag_data/transmissiongate.md new file mode 100644 index 000000000..6ad2331bb --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/transmissiongate.md @@ -0,0 +1,43 @@ +# Transmission Gate + +## Purpose + +A transmission gate is a bidirectional analog switch that uses a pair of complementary transistors to pass or block signals. It operates as an effective switch in digital and analog circuits, allowing the isolation or connection of circuit nodes depending on the control signal. Transmission gates are fundamental components in CMOS digital logic, often used in multiplexers, switches, and data buses. + +## Terms Defined + +Pass Gate: Alternative name for a transmission gate.
+CMOS: Complementary Metal-Oxide-Semiconductor, a technology for constructing integrated circuits using a pair of p-type and n-type MOSFETs.
+nMOS (n-channel MOSFET): A type of MOSFET that conducts when a positive voltage is applied to the gate relative to the source.
+pMOS (p-channel MOSFET): A type of MOSFET that conducts when a negative voltage is applied to the gate relative to the source.
+Control Voltage (V_control): The voltage applied to the gates of the MOSFETs to open or close the transmission gate. + +## Theory + +In a transmission gate, the complementary MOSFETs are used to create a switch that can pass both high and low logic levels effectively. This is because nMOS transistors pass strong '0's (low voltages) and weak '1's (high voltages) while pMOS transistors pass strong '1's and weak '0's. By pairing an nMOS with a pMOS, the transmission gate can pass both logic levels with low resistance. + +## Schematic + +### In Words + +The transmission gate consists of an nMOS and a pMOS transistor connected in parallel, with their drains and sources tied together, respectively. The gate of the nMOS transistor connects to the control voltage, while the gate of the pMOS transistor connects to the inverse of the control voltage. This arrangement ensures that both transistors are either on or off simultaneously. + +### Pseudo Netlist + +A transmission gate has the following subckt definition: .subckt transmissiongate input output control gnd vdd +Mn1 output input gnd gnd nMOS +Mp1 output input vdd vdd pMOS +.endsubckt + +Here, Mn1 represents the nMOS transistor with terminals connected to output, input, gnd (source), and gnd (body), while Mp1 represents the pMOS transistor with terminals connected to output, input, vdd (source), and vdd (body). control ties to the gate of the nMOS, and the inverted control ties to the gate of the pMOS. + +## Performance Specifications + +Important performance characteristics for transmission gates include: + +On Resistance (R_on): The resistance of the TG when it is in the conducting state, ideally as low as possible to reduce voltage drop and power dissipation.
+Off State Leakage: The current that flows through the TG when it is supposed to be off. It should be minimized to prevent unintended signal pass-through. +Switching Speed: The time it takes for the TG to transition from on to off (and vice versa), which influences the maximum operating frequency.
+Charge Injection: The amount of charge shared between the gate and the channel during switching, which can cause glitches in the output.
+Signal Integrity: The ability of the TG to pass the signal without distortion, which is influenced by factors such as channel length modulation and body effect. + diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/voltagedivider.md b/openfasoc/generators/glayout/glayout/llm/rag_data/voltagedivider.md new file mode 100644 index 000000000..c4eca30b7 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/voltagedivider.md @@ -0,0 +1,37 @@ +# Voltage Divider +## Purpose + +A voltage divider is a linear circuit that converts a high voltage into a lower one using two series resistors. The primary function of a voltage divider is to produce a voltage that is a fraction of the input voltage, making it useful for creating reference voltages, reducing voltage to safe levels for measurement or for circuit operation, and for biasing of transistors in amplifiers and other devices. + +## Terms Defined + +R1: The first resistor in the series connection, closest to the voltage source.
+R2: The second resistor in the series, connected between R1 and ground.
+Vin: Input voltage applied across the series combination of R1 and R2.
+Vout: Output voltage taken across R2. + +## Theory +The voltage divider works on the principle of resistive scaling. When a voltage is applied across a series of resistors, the voltage drop across each resistor is proportional to its resistance. The basic formula for a two-resistor voltage divider is derived from Ohm's Law $(V = I * R)$ and Kirchhoff's laws. The output voltage (Vout) across R2 is given by: + +$[ V_{out} = V_{in} \times \frac{R2}{R1 + R2} ]$ + +This relationship shows that the output voltage is a fraction of the input voltage, with the fraction determined by the relative values of R1 and R2. + +## Schematic + +### Described in Words + +In a voltage divider schematic, the input voltage Vin is connected to the first terminal of R1. The second terminal of R1 is connected to the first terminal of R2, and the second terminal of R2 is grounded. Vout is the voltage drop measured across R2, which is a result of the partial pressure provided by the resistors R1 and R2. +### Pseudo Netlist + +A pseudo netlist of a voltage divider can be written as: .subckt voltagedivider Vin Vout GND R1 Vin Vout R2 Vout GND .endsubckt + +## Performance Specifications + +The key specifications for a voltage divider include: + +Voltage Output (Vout): Can be calculated with the formula Vout = Vin * (R2 / (R1 + R2)).
+Resistor Values (R1 and R2): Chosen based on the desired output voltage and current requirements. They affect the power rating and physical size of the resistors.
+Power Dissipation: Determined by the voltage drop and the current through each resistor. Each resistor must have a power rating that can handle the power it dissipates without overheating.
+Load Effect: The voltage divider ideally works for no-load or high-impedance load conditions. A connected load can alter the expected Vout if not considered properly.
+ diff --git a/openfasoc/generators/glayout/glayout/llm/rag_data/voltagefollower.md b/openfasoc/generators/glayout/glayout/llm/rag_data/voltagefollower.md new file mode 100644 index 000000000..91ac5efe5 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/rag_data/voltagefollower.md @@ -0,0 +1,50 @@ +# Voltage Follower + +## Purpose + +A voltage follower, also known as a buffer amplifier, is a circuit configuration where the output voltage directly follows the input voltage. This means the output voltage is the same as the input voltage. The primary purpose of a voltage follower is to increase the input impedance (ideally to infinity) and decrease the output impedance (ideally to zero), thus providing no voltage gain but significant current gain. This allows the voltage follower to serve as a buffer, isolating the source from the load while preventing signal attenuation that would occur if the load were directly connected to the source. + +## Terms Defined + +Anode: The terminal through which conventional current flows into the diode, typically marked with a plus sign. +Cathode: The terminal through which conventional current flows out of the diode, often denoted with a line or band on the diode body. +Forward Bias: A condition where the anode is more positive relative to the cathode, allowing current flow. +Reverse Bias: A condition where the cathode is more positive in relation to the anode, restricting current flow. +Forward Voltage Drop (Vf): The potential difference across the diode terminals when current is conducted in the forward direction, typically 0.7V for silicon diodes and 0.3V for germanium diodes. +Reverse Breakdown Voltage (V_br): The voltage at which the diode will conduct a significant reverse current, potentially leading to device damage if sustained. + +## Theory + +The voltage follower is realized using an operational amplifier (op-amp) with 100% negative feedback provided by a direct connection from the output terminal back to the inverting input. There is no external feedback network of resistors or capacitors, simplifying the configuration. + +## Schematic + +### In Words + +In the schematic for a voltage follower: + +The positive (+) terminal of the op-amp is the non-inverting input. +The negative (−) terminal of the op-amp, the inverting input, is connected directly to the output terminal of the op-amp. +The input voltage is applied to the non-inverting input (+). +The output is taken from the output terminal of the op-amp. + + +### Pseudo Netlist + +An operational amplifier has the following subckt definition: .subckt opamp inverting_input non_inverting_input output v_positive v_negative + +A voltage follower has the following subckt definition: .subckt voltagefollower in out opamp_model X1 in out out opamp_model .endsubckt + +X1 represents the operational amplifier with the in net connected to the non-inverting input, the out net connected to both the inverting input and the output, with opamp_model defining the operational amplifier’s characteristics. + +## Performance Specifications + +Key specifications for a voltage follower include: + +Input Impedance (Z_in): Ideally infinite, which means no current is drawn from the input source.
+Output Impedance (Z_out): Ideally zero, which allows the circuit to drive heavy loads without significant voltage drop.
+Voltage Gain (A_v): Unity (1), meaning the output voltage equals the input voltage without amplification.
+Bandwidth: Broad, often limited only by the op-amp’s characteristics since no reactive components are introduced.
+Slew Rate: This defines the maximum rate at which the output can change and can be a limiting factor in high-frequency applications. + +Voltage followers are widely used in circuits where signal isolation is needed without altering the signal voltage, such as interfacing between high and low impedance circuit blocks or driving capacitive loads without stability issues. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/GlayoutStrictSyntax.md b/openfasoc/generators/glayout/glayout/llm/syntax_data/GlayoutStrictSyntax.md new file mode 100644 index 000000000..23179a45b --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/GlayoutStrictSyntax.md @@ -0,0 +1,13 @@ +# What is Glayout strictsyntax? +Glayout strictsyntax is a command language for producing analog circuit layout. +## commands and syntax +Below are some example commands +place command: place an genidhere called/named compnamehere with paramshere +route command: route between/from port1 and/to port2 using routetype with paramshere +absolute move command: move compname [by] (x,y) +relative move command: move compname [filler words] direction [filler words] reference_comp [by/with separation] +import command: import comp1, comp2 from mod1, and comp3 from some/path/mod.py +NOTE: imports from glayout only need component name +NOTE: if mod name is not specified, it is assumed to be the component name +create parameter command: create/define [a/an] param_type parameter called/named paramname +create variable: create/define [a/an] var_type variable called/named varname =/equal valorexpr diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/Sakib_testcases.json b/openfasoc/generators/glayout/glayout/llm/syntax_data/Sakib_testcases.json new file mode 100644 index 000000000..ddc49bc66 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/Sakib_testcases.json @@ -0,0 +1,84 @@ +{ + "data": [ + { + "NLPfilename": "BiasVoltageGenerator.convo", + "LLMprompt": "layout a pmos called m1 with width width_m1, length length_m1, fingers m1_fingers, and multipliers m1_multipliers. layout a nmos called m2 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. Short the drains of both transistors, and connect the gate of m2 to this short. Connect the gate of m1 to the source of m2." + }, + { + "NLPfilename": "CascodeCommonGate1.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Short the source of m1 and the drain of m2." + }, + { + "NLPfilename": "CascodeCommonSource.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the source of m2 and the drain of m1. Short the ties of the transistors." + }, + { + "NLPfilename": "ClassBPushPull.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another pmos called m2 with a different set of parameters that are suffixed by m2. Short the sources of the transistors, and short the gates of the transistors." + }, + { + "NLPfilename": "CommonSourceAmplifier.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another pmos called m2 with a different set of parameters that are suffixed by m2. Connect the drain of m1 to the source of m2." + }, + { + "NLPfilename": "CommonSourceAmplifier1.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the source of m2 and the drain of m1. Connect the gate and drain of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "CommonSourceAmplifier2.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Short the gate and drain of m2. Connect the sources of both transistors. Short the drains of both transistors. Connect the ties of both transistors." + }, + { + "NLPfilename": "CommonSourceAmplifier2.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Short the gate and drain of m2. Connect the sources of both transistors. Short the drains of both transistors. Connect the ties of both transistors." + }, + { + "NLPfilename": "ConstBiasVoltageGen.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the source of m2 and the drain of m1. Connect the gate and drain of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "DegenCommonGate.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the source of m2 and the drain of m1. Connect the gate and drain of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "DegenCommonSource.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the source of m1 and the drain of m2. Connect the gates of m1 and m2. Short the ties of both transistors." + }, + { + "NLPfilename": "LowNoiseAmp.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the source of m1 and the drain of m2. Connect the source of m1 to the drain of m2. Connect the gate of m1 to the source of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "NoiseXDiffConv.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the gate of m1 to the source of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "PTATVoltageGen.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the source of m2 and the drain of m1. Connect the gates of both transistors. Short the gate and drain of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "CMbasic.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Connect the drain of m1 to the gate of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "cmirror_absmove.convo", + "LLMprompt": "layout a nmos called reference with width 5, and another nmos called mirror with width 10. Move mirro to the coordinate (5,5)" + }, + { + "NLPfilename": "cmirror_relmove.convo", + "LLMprompt": "layout a nmos called reference with width 5, and another nmos called mirror with width 10. Move mirror to the left of reference." + }, + { + "NLPfilename": "CommonSourceBE.convo", + "LLMprompt": "layout a nmos called m1 with width width_m2, length length_m2, fingers m2_fingers, and multipliers m2_multipliers. layout another nmos called m2 with a different set of parameters that are suffixed by m2. Short the drains of m1 and m2. Route between the drain of m1 and the gate of m2. Connect the ties of both transistors." + }, + { + "NLPfilename": "CrossCoupledInverters.convo", + "LLMprompt": "layout two inverters, with the nmos and the pmos of both transistors being interdigitated. Short the sources of the interdigitated nmos' and short the sources for the interdigitated pmos'. Connect the output of the opposite inverter to the input of the inverter." + }, + { + "NLPfilename": "CrossCoupledPair.convo", + "LLMprompt": "layout an interdigitated nmos. Connect the gate of A of that nmos to the drain of B. Connect the gate of B to the drain of A. Short the sources of A and B." + } + ] +} diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/All2TInterdigitizedSmartRoutes.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/All2TInterdigitizedSmartRoutes.convo new file mode 100644 index 000000000..ee442d335 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/All2TInterdigitizedSmartRoutes.convo @@ -0,0 +1,37 @@ +CrossCoupledInverters +Create a float parameter called cc_invs_nfet_width +Create a float parameter called cc_invs_pfet_width +Create a float parameter called cc_invs_nfet_length +Create a float parameter called cc_invs_pfet_length +Create an int parameter called cc_invs_numfingers +place an interdigitated nfet called cc_nfets with with_substrate_tap=False, numcols=cc_invs_numfingers , kwargs={ "width" : cc_invs_nfet_width , "length" : cc_invs_nfet_length } +place an interdigitated pfet called cc_pfets with numcols=cc_invs_numfingers , kwargs={ "width" : cc_invs_pfet_width , "length" : cc_invs_pfet_length } +move cc_pfets above cc_nfets +# route between cc_pfets_substratetap_S_top_met_S and cc_nfets_welltie_N_top_met_N using straight_route +# route between cc_nfets_A_gate_E and cc_pfets_A_gate_E using c_route +# route between cc_nfets_B_gate_W and cc_pfets_B_gate_W using c_route +# route between cc_nfets_A_source_E and cc_pfets_A_source_E using c_route with extension=1 +# route between cc_nfets_B_source_W and cc_pfets_B_source_W using c_route with extension=1 +# route between cc_nfets_A_drain_E and cc_nfets_B_gate_E using c_route with extension=1.5 +# route between cc_pfets_A_drain_E and cc_pfets_B_gate_E using c_route with extension=1.5 +# +# +# self routes 6/15 +route between cc_pfets_A_drain_E and cc_pfets_A_gate_E +route between cc_pfets_A_drain_E and cc_pfets_A_source_E +route between cc_pfets_A_source_E and cc_pfets_A_gate_E +route between cc_pfets_B_drain_E and cc_pfets_B_gate_E +route between cc_pfets_B_drain_E and cc_pfets_B_source_E +route between cc_pfets_B_source_E and cc_pfets_B_gate_E +# inter transistor s/d routes 4/15 +route between cc_pfets_B_source_E and cc_pfets_A_source_E +route between cc_pfets_B_drain_E and cc_pfets_A_drain_E +route between cc_pfets_B_source_E and cc_pfets_A_drain_E +route between cc_pfets_B_drain_E and cc_pfets_A_source_E +## inter transistor s/d to gate routes 4/15 +route between cc_pfets_B_gate_E and cc_pfets_A_source_E +route between cc_pfets_B_gate_E and cc_pfets_A_drain_E +route between cc_pfets_A_gate_E and cc_pfets_B_source_E +route between cc_pfets_A_gate_E and cc_pfets_B_drain_E +# inter transistor gate to gate route 1/15 +route between cc_pfets_B_gate_E and cc_pfets_A_gate_E \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/BiasVGen.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/BiasVGen.convo new file mode 100644 index 000000000..425f1eedb --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/BiasVGen.convo @@ -0,0 +1,11 @@ +BiasVGen +create a float parameter called width_m1 +create a float parameter called width_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a float parameter called maxmetalsep +place a nfet called m1 with width width_m1 multipliers m1_multiplier with_substrate_tap False with_dnwell False +place a nfet called m2 with width width_m2 multipliers m2_multiplier with_substrate_tap False with_dnwell False +move m2 below m1 +route between m1_multiplier_0_drain_E and m2_multiplier_0_drain_E using c_route with extension=maxmetalsep +route between m1_multiplier_0_drain_W and m2_multiplier_0_gate_W using c_route with extension=maxmetalsep diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/BiasVoltageGenerator.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/BiasVoltageGenerator.convo new file mode 100644 index 000000000..886711ebd --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/BiasVoltageGenerator.convo @@ -0,0 +1,15 @@ +BiasVoltageGenerator +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a pmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m2 below m1 +route between m1_multiplier_0_drain_E and m2_multiplier_0_drain_E using c_route with extension=4 +route between m2_multiplier_0_gate_E and m2_multiplier_0_drain_E using c_route with extension=2 +route between m1_multiplier_0_gate_W and m2_multiplier_0_source_W using c_route with extension=3 \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CMbasic.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CMbasic.convo new file mode 100644 index 000000000..3f7b9d523 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CMbasic.convo @@ -0,0 +1,10 @@ +CMbasic +create float parameter called m1_width +create float parameter called m2_width +create an int parameter called m1_multiplier +create an int parameter called m2_multiplier +place an nmos called m1 with width m1_width length 5 multipliers m1_multiplier +place a nmos called m2 with width m2_width length 5 multipliers m2_multiplier +move m2 to the right of m1 +route between m1_multiplier_0_gate_E and m2_multiplier_0_gate_E using smart_route +route between m2_multiplier_0_drain_E and m2_multiplier_0_gate_E using smart_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CTATVGen.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CTATVGen.convo new file mode 100644 index 000000000..3d47f7c79 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CTATVGen.convo @@ -0,0 +1,10 @@ +CTATVGen +create a float parameter called width_m1 +create a float parameter called width_m2 +create a int parameter called m2_multiplier +create a int parameter called m1_multiplier +place a nmos called m1 with width width_m1 multipliers m1_multiplier with_substrate_tap False with_dnwell False +place a nmos called m2 with width width_m2 multipliers m2_multiplier with_substrate_tap False with_dnwell False +move m2 below m1 +route between m1_multiplier_0_drain_N and m2_multiplier_0_source_N using smart_route +route between m1_multiplier_0_drain_N and m2_multiplier_0_source_N using smart_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CascodeCommonGate1.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CascodeCommonGate1.convo new file mode 100644 index 000000000..7db7ce6bb --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CascodeCommonGate1.convo @@ -0,0 +1,7 @@ +CascodeCommonGate +create float parameter called width_m1 +create float parameter called width_m2 +place an nfet called m1 with width width_m1 +place an nfet called m2 with width width_m2 +move m2 above m1 +route between m1_multiplier_0_source_W and m2_multiplier_0_drain_W using c_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CascodeCommonSource.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CascodeCommonSource.convo new file mode 100644 index 000000000..006cf9ec8 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CascodeCommonSource.convo @@ -0,0 +1,14 @@ +CascodeCommonSource +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m1 below m2 +route between m2_multiplier_0_source_W and m1_multiplier_0_drain_W using c_route with extension=3 +route between m1_tie_br_top_met_S and m2_tie_tr_top_met_N using straight_route \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ClassBPushPull.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ClassBPushPull.convo new file mode 100644 index 000000000..88e4f7c2a --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ClassBPushPull.convo @@ -0,0 +1,14 @@ +ClassBPushPull +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a pmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True +move m2 below m1 +route between m1_multiplier_0_source_W and m2_multiplier_0_source_W using c_route with extension=3 +route between m1_multiplier_0_gate_E and m2_multiplier_0_gate_E using c_route with extension=3 \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier.convo new file mode 100644 index 000000000..754e6b985 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier.convo @@ -0,0 +1,13 @@ +CommonSourceAmplifier +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a pmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True +move m1 below m2 +route between m1_multiplier_0_drain_W and m2_multiplier_0_source_W using c_route with extension=3 \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier1.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier1.convo new file mode 100644 index 000000000..3eb006c8b --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier1.convo @@ -0,0 +1,15 @@ +CommonSourceAmplifier1 +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m1 below m2 +route between m2_multiplier_0_source_W and m1_multiplier_0_drain_W using c_route with extension=3 +route between m2_multiplier_0_gate_E and m2_multiplier_0_drain_E using c_route with extension=2 +route between m1_tie_br_top_met_S and m2_tie_tr_top_met_N using straight_route \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier2.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier2.convo new file mode 100644 index 000000000..ad2371ff7 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceAmplifier2.convo @@ -0,0 +1,16 @@ +CommonSourceAmplifier2 +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m2 below m1 +route between m2_multiplier_0_gate_E and m2_multiplier_0_drain_E using c_route with extension=2 +route between m1_multiplier_0_source_W and m2_multiplier_0_source_W using c_route with extension=3 +route between m1_multiplier_0_drain_E and m2_multiplier_0_drain_E using c_route with extension=4 +route between m1_tie_br_top_met_S and m2_tie_bl_top_met_N using straight_route \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceBE.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceBE.convo new file mode 100644 index 000000000..bd8942836 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CommonSourceBE.convo @@ -0,0 +1,13 @@ +CommonSourceBE +create float parameter called m1_width +create float parameter called m2_width +create an int parameter called m1_multiplier +create an int parameter called m2_multiplier +place an nmos called m1 with width m1_width length 3 multipliers m1_multiplier +place a nmos called m2 with width m2_width length 3 multipliers m2_multiplier +move m2 to the right of m1 +route between m1_multiplier_0_drain_N and m2_multiplier_0_gate_N using c_route +route between m1_multiplier_0_drain_W and m2_multiplier_0_drain_E using straight_route + + + diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ConstBiasVoltageGen.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ConstBiasVoltageGen.convo new file mode 100644 index 000000000..f311df0b9 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ConstBiasVoltageGen.convo @@ -0,0 +1,16 @@ +ConstBiasVoltageGen +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m2 below m1 +route between m1_multiplier_0_source_E and m2_multiplier_0_drain_E using c_route with extension=4 +route between m2_multiplier_0_gate_E and m2_multiplier_0_drain_E using c_route with extension=2 +route between m1_multiplier_0_gate_W and m2_multiplier_0_source_W using c_route with extension=3 +route between m1_tie_br_top_met_S and m2_tie_bl_top_met_N using straight_route \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CrossCoupledInverters.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CrossCoupledInverters.convo new file mode 100644 index 000000000..8603e7f70 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CrossCoupledInverters.convo @@ -0,0 +1,18 @@ +CrossCoupledInverters +#Create a float parameter called ccinvs_nfet_width +#Create a float parameter called ccinvs_pfet_width +#Create a float parameter called ccinvs_nfet_length +#Create a float parameter called ccinvs_pfet_length +Create an int parameter called ccinvs_numfingers +place 4 interdigitated transistors called ccinvs with numcols=ccinvs_numfingers, top_row_device="pfet", bottom_row_device="nfet" +# sources are connected to pwr and gnd respectively +route between ccinvs_top_A_source_E and ccinvs_top_B_source_E +route between ccinvs_bottom_A_source_E and ccinvs_bottom_B_source_E +# output of each inverter goes to input of the other inverter +route between ccinvs_top_A_drain_E and ccinvs_top_B_gate_E +route between ccinvs_bottom_A_drain_E and ccinvs_bottom_B_gate_E +route between ccinvs_top_B_drain_E and ccinvs_top_A_gate_E +route between ccinvs_bottom_B_drain_E and ccinvs_bottom_A_gate_E +# connect nfet and pfet of each inverter at the gate +route between ccinvs_top_B_gate_E and ccinvs_bottom_B_gate_E +route between ccinvs_top_A_gate_W and ccinvs_bottom_A_gate_W \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CurrentMirror.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CurrentMirror.convo new file mode 100644 index 000000000..27f30021e --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CurrentMirror.convo @@ -0,0 +1,13 @@ +CMbasic +create float parameter called m1_width +create float parameter called m2_width +create an int parameter called m1_multiplier +create an int parameter called m2_multiplier +place an nmos called m1 with width m1_width length 10 rmult m1_multiplier +place a nmos called m2 with width m2_width length 10 rmult m2_multiplier +route between m1_multiplier_0_drain_tr and m2_multiplier_0_gate_bl using straight_route +route between m1_tie_br_top_met_W and m2_tie_bl_top_met_E using straig +place an nmos called m1 with width 5 and length 10 +place a nmos called m2 with width 10 and length 10 +route between m1_multiplier_0_drain_tr and m2_multiplier_0_gate_bl using l_route +route between m1_multiplier_0_gate_br and m2_multiplier_0_gate_bl using straight_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CurrentMirrorInterdigitized.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CurrentMirrorInterdigitized.convo new file mode 100644 index 000000000..4fb184066 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/CurrentMirrorInterdigitized.convo @@ -0,0 +1,16 @@ +CrossCoupledInverters +# create parameters in alphabetical order +Create a float parameter called length +Create an int parameter called numfingers +Create a float parameter called width +# place list out all parameters (some are different than default) in order they appear in the function call +# use a/an depending on english grammar (an before a vowel) +place an interdigitated nfet called cmirror with numcols=numfingers, dummy=True, with_substrate_tap=False, with_tie=True, tie_layers=("met2","met1"), kwargs={ "width" : width , "length" : length } +# route between ... and ... +# if you dont specify anything that means you are doing smart_route on a supported circuit +# which means you do not need to specify a direction, in this case always set direction to East and dont specify route type +# if you have to specify a direction, then always specify route type (even if you are using smart_route) +route between cmirror_A_drain_E and cmirror_A_gate_E +route between cmirror_A_gate_E and cmirror_B_gate_E +route between cmirror_A_source_E and cmirror_B_source_E +# coming soon, also route ground/power \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DegenCommonGate.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DegenCommonGate.convo new file mode 100644 index 000000000..f11615397 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DegenCommonGate.convo @@ -0,0 +1,14 @@ +DegenCommonGate +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m2 below m1 +route between m1_multiplier_0_source_W and m2_multiplier_0_drain_W using c_route with extension=3 +route between m2_tie_br_top_met_S and m1_tie_tr_top_met_N using straight_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DegenCommonSource.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DegenCommonSource.convo new file mode 100644 index 000000000..71f221660 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DegenCommonSource.convo @@ -0,0 +1,15 @@ +DegenCommonSource +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m2 below m1 +route between m1_multiplier_0_source_W and m2_multiplier_0_drain_W using c_route with extension=3 +route between m1_multiplier_0_gate_E and m2_multiplier_0_gate_E using c_route with extension=2 +route between m2_tie_br_top_met_S and m1_tie_tr_top_met_N using straight_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DiffPair.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DiffPair.convo new file mode 100644 index 000000000..dbbd5c71b --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/DiffPair.convo @@ -0,0 +1,11 @@ +DiffPair +create float parameter called m1_width +create float parameter called m2_width +create an int parameter called m1_multiplier +create an int parameter called m2_multiplier +create an int parameter called m1_fingers +create an int parameter called m2_fingers +place an nfet called m1 with width m1_width length 10 rmult m1_multiplier fingers m1_fingers +place an nfet called m2 with width m2_width length 10 rmult m2_multiplier fingers m2_fingers +move m1 to the left of m2 + diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/LowNoiseAmp.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/LowNoiseAmp.convo new file mode 100644 index 000000000..c0ab13820 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/LowNoiseAmp.convo @@ -0,0 +1,16 @@ +LowNoiseAmp +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m2 below m1 +route between m1_multiplier_0_source_W and m2_multiplier_0_drain_W using c_route with extension=3 +route between m1_multiplier_0_gate_E and m2_multiplier_0_source_E using c_route with extension=2 +route between m2_tie_br_top_met_S and m1_tie_tr_top_met_N using straight_route + diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/NoiseXDiffConv.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/NoiseXDiffConv.convo new file mode 100644 index 000000000..ec3156b9e --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/NoiseXDiffConv.convo @@ -0,0 +1,14 @@ +NoiseXDiffConv +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m1 below m2 +route between m1_multiplier_0_gate_E and m2_multiplier_0_source_E using c_route with extension=2 +route between m1_tie_br_top_met_S and m2_tie_tr_top_met_N using straight_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/PTATVoltageGen.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/PTATVoltageGen.convo new file mode 100644 index 000000000..5bdb95f76 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/PTATVoltageGen.convo @@ -0,0 +1,15 @@ +PTATVoltageGen +create a float parameter called width_m1 +create a float parameter called width_m2 +create a float parameter called length_m1 +create a float parameter called length_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nmos called m1 with width width_m1 length length_m1 fingers m1_fingers rmult 1 multipliers m1_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +place a nmos called m2 with width width_m2 length length_m2 fingers m2_fingers rmult 1 multipliers m2_multiplier with_substrate_tap False with_tie True sd_rmult 1 gate_rmult 1 interfinger_rmult 1 with_dummy True with_dnwell False +move m1 below m2 +route between m2_multiplier_0_source_E and m1_multiplier_0_drain_E using c_route with extension=2 +route between m2_multiplier_0_gate_W and m1_multiplier_0_gate_W using c_route with extension=3 +route between m2_multiplier_0_gate_W and m2_multiplier_0_drain_W using c_route with extension=5 \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/RegCascode.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/RegCascode.convo new file mode 100644 index 000000000..093da73c7 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/RegCascode.convo @@ -0,0 +1,10 @@ +RegCascode +create a float parameter called width_m1 +create a float parameter called width_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +place a nfet called m1 with width width_m1 multipliers m1_multiplier with_substrate_tap False with_dnwell False +place a nfet called m2 with width width_m2 multipliers m2_multiplier with_substrate_tap False with_dnwell False +move m2 left of m1 +route between m1_multiplier_0_gate_N and m2_multiplier_0_drain_N using smart_route +route between m2_multiplier_0_gate_E and m2_multiplier_0_source_N using smart_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/SourceFollow.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/SourceFollow.convo new file mode 100644 index 000000000..f5f7f6498 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/SourceFollow.convo @@ -0,0 +1,9 @@ +SourceFollow +create float parameter called m1_width +create float parameter called m2_width +create an int parameter called m1_multiplier +create an int parameter called m2_multiplier +place an nmos called m1 with width m1_width length 3 multipliers m1_multiplier +place a nmos called m2 with width m2_width length 3 multipliers m2_multiplier +move m2 below m1 +route between m1_multiplier_0_source_S and m2_multiplier_0_drain_N using straight_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/StrongArmLatch.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/StrongArmLatch.convo new file mode 100644 index 000000000..a26b16d2d --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/StrongArmLatch.convo @@ -0,0 +1,29 @@ +StrongArmLatch +import CrossCoupledInverters +place a diff pair called inputdiff +place CrossCoupledInverters called ccinvs with ccinvs_numfingers=4 +place a nfet called bridge with with_substrate_tap=False with_dnwell=False +place a nfet called clkgnd with with_substrate_tap=False with_dnwell=False +place a pfet called clkpwrL +place a pfet called clkpwrR +move clkgnd below inputdiff +move bridge above inputdiff +move ccinvs above bridge +move clkpwrR to the right of ccinvs +move clkpwrR above bridge +move clkpwrL to the left of ccinvs +move clkpwrL above bridge +# route bridge to inputdiff +route between inputdiff_A_drain_E and bridge_multiplier_0_drain_E +route between inputdiff_B_drain_W and bridge_multiplier_0_source_W +# route inputdiff with clkgnd +route between inputdiff_A_source_E and clkgnd_multiplier_0_drain_E +# route ccinvs to clkpwr +route between ccinvs_ccinvs_top_A_drain_W and clkpwrL_multiplier_0_drain_W +route between ccinvs_ccinvs_top_B_source_E and clkpwrR_multiplier_0_drain_E +# route bridge to ccinvs +route between bridge_multiplier_0_source_W and ccinvs_ccinvs_bottom_A_source_W +route between bridge_multiplier_0_drain_E and ccinvs_ccinvs_bottom_B_source_E +# clock routing +route between clkgnd_multiplier_0_gate_W and clkpwrL_multiplier_0_gate_W +route between clkgnd_multiplier_0_gate_E and clkpwrR_multiplier_0_gate_E \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ULPD.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ULPD.convo new file mode 100644 index 000000000..53f670227 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/ULPD.convo @@ -0,0 +1,12 @@ +ULPD +create a float parameter called width_m1 +create a float parameter called width_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +create a int parameter called m1_fingers +create a int parameter called m2_fingers +place a nfet called m1 with width width_m1 multipliers m1_multiplier with_substrate_tap False with_dnwell False fingers m1_fingers +place a nfet called m2 with width width_m2 multipliers m2_multiplier with_substrate_tap False with_dnwell False fingers m2_fingers +move m2 right of m1 +route between m1_multiplier_0_gate_N and m2_multiplier_0_drain_N using smart_route +route between m1_multiplier_0_drain_N and m2_multiplier_0_gate_N using smart_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/Varactor.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/Varactor.convo new file mode 100644 index 000000000..477c617c9 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/Varactor.convo @@ -0,0 +1,11 @@ +Varactor +create a float parameter called width_m1 +create a float parameter called width_m2 +create a int parameter called m2_multiplier +create a int parameter called m1_multiplier +place a nfet called m1 with width width_m1 multipliers m1_multiplier with_substrate_tap False with_dnwell False +place a nfet called m2 with width width_m2 multipliers m2_multiplier with_substrate_tap False with_dnwell False +move m2 to the right of m1 +route between m1_multiplier_0_drain_N and m2_multiplier_0_drain_N using smart_route +route between m1_multiplier_0_source_N and m2_multiplier_0_source_N using smart_route +route between m1_multiplier_0_source_N and m2_multiplier_0_drain_N using smart_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/VoltageFollower.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/VoltageFollower.convo new file mode 100644 index 000000000..1c847663e --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/VoltageFollower.convo @@ -0,0 +1,10 @@ +VoltageFollower +create a float parameter called width_m1 +create a float parameter called width_m2 +create a int parameter called m1_multiplier +create a int parameter called m2_multiplier +place a nfet called m1 with width width_m1 multipliers m1_multiplier with_substrate_tap False with_dnwell False +place a nmos called m2 with width width_m2 multipliers m2_multiplier with_substrate_tap False with_dnwell False +move m2 below m1 +route between m1_multiplier_0_source_W and m2_multiplier_0_gate_W using c_route with extension=2 +route between m2_multiplier_0_drain_S and m2_multiplier_0_gate_N using straight_route diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/cmirror_absmove.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/cmirror_absmove.convo new file mode 100644 index 000000000..4e9a80a58 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/cmirror_absmove.convo @@ -0,0 +1,4 @@ +cmirror +place an nfet called reference with width 5 +place an nfet called mirror with width 10 +move mirror by 5,5 diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/cmirror_relmove.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/cmirror_relmove.convo new file mode 100644 index 000000000..e430b84ed --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/cmirror_relmove.convo @@ -0,0 +1,4 @@ +cmirror +place an nfet called reference with width 5 +place an nfet called mirror with width 10 +move mirror to the left of reference diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/inverter_4.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/inverter_4.convo new file mode 100644 index 000000000..3c30f61a6 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/inverter_4.convo @@ -0,0 +1,4 @@ +inverter +Place an pmos called pullup with width 10 and length 10 +Place a nmos called pulldown with width 10 and length 10 +Move pulldown below pullup with separation 0.4 diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/opamp.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/opamp.convo new file mode 100644 index 000000000..b54868553 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/opamp.convo @@ -0,0 +1,67 @@ +opamp +import diff_pair from glayout.flow.components.diff_pair +import generic_4T_interdigitzed from glayout.flow.placement.four_transistor_interdigitized +import current_mirror from glayout.flow.components.current_mirror +create an int parameter called numcols +place generic_4T_interdigitzed called mydiffload with numcols numcols top_kwargs {'tie_layers':("met3","met2")}top_row_device 'pfet' bottom_row_device 'pfet' +place a diff_pair called mydiff with width 5 fingers numcols dummy True substrate_tap True +move mydiffload to north of mydiff +route between mydiffload_top_A_source_W and mydiffload_top_B_source_W +route between mydiffload_top_A_gate_W and mydiffload_top_B_gate_W using c_route with extension=2 +route between mydiffload_bottom_A_gate_W and mydiffload_bottom_B_gate_W using c_route with extension=2 +route between mydiffload_bottom_A_gate_E and mydiffload_top_A_gate_E +route between mydiffload_bottom_A_gate_E and mydiffload_top_A_gate_E +route between mydiffload_top_A_drain_W and mydiffload_bottom_A_source_W +route between mydiffload_top_B_drain_E and mydiffload_bottom_B_source_E using c_route with extension=2 +route between mydiffload_bottom_B_drain_W and mydiffload_bottom_B_gate_W +route between mydiffload_top_A_drain_W and mydiff_bl_multiplier_0_drain_W using c_route with extension=4 +route between mydiffload_top_B_drain_E and mydiff_br_multiplier_0_drain_E using c_route with extension=4 +place a current_mirror called mycurrmirror with numcols numcols device 'nfet' with_dummy False +move mycurrmirror to south of mydiff +route between mydiff_bl_multiplier_0_source_W and mycurrmirror_fet_B_0_source_W using c_route with extension=3 +create a float parameter called load_pfet_width +create an int parameter called load_pfet_fingers +create an int parameter called load_pfet_multipliers +place a pfet called load_pfet_left with width load_pfet_width fingers load_pfet_fingers multipliers load_pfet_multipliers +# move load_pfet_left by -20,20 +move load_pfet_left to left of mydiff +move load_pfet_left to north of mydiff +move load_pfet_left to left of mydiffload +place a pfet called load_pfet_right with width load_pfet_width fingers load_pfet_fingers multipliers load_pfet_multipliers +# move load_pfet_right by 20,20 +move load_pfet_right to right of mydiff +move load_pfet_right to north of mydiff +move load_pfet_right to right of mydiffload +route between load_pfet_right_multiplier_0_gate_con_N and load_pfet_left_multiplier_0_gate_con_N using c_route with extension=18 width1=0.8 width2=0.8 +route between load_pfet_right_multiplier_0_drain_con_N and load_pfet_left_multiplier_0_drain_con_N using c_route with extension=8 width1=0.8 width2=0.8 +route between load_pfet_right_multiplier_0_source_con_N and load_pfet_left_multiplier_0_source_con_N using c_route with extension=5.5 viaoffset=(True, False) fullbottom=True width1=0.8 width2=0.8 +create an int parameter called load_curr_source_fingers +create a float parameter called load_curr_source_width_ref +create a float parameter called load_curr_source_width_mirr +place an nfet called load_curr_source_ref with width load_curr_source_width_ref fingers load_curr_source_fingers multipliers 2 +move load_curr_source to left of mydiff +move load_curr_source_ref to the south mydiff +# move load_curr_source_ref by -20,0 +move load_curr_source_ref to left of mycurrmirror +place an nfet called load_curr_source_mirr with width load_curr_source_width_mirr fingers load_curr_source_fingers multipliers 2 +move load_curr_source_mirr to right of mydiff +move load_curr_source_mirr to the south mydiff +move load_curr_source_mirr to right of mycurrmirror +# move load_curr_source_mirr by 20,0 +route between load_curr_source_ref_multiplier_0_gate_con_S and load_curr_source_mirr_multiplier_0_gate_con_S using c_route with extension=3 width1=0.8 width2=0.8 +route between load_curr_source_ref_multiplier_0_drain_con_S and load_curr_source_mirr_multiplier_0_drain_con_S using c_route with extension=12 viaoffset=(False, True) width1=0.8 width2=0.8 +route between load_curr_source_ref_multiplier_0_source_con_S and load_curr_source_mirr_multiplier_0_source_con_S using c_route with extension=14 width1=0.8 width2=0.8 +route between load_curr_source_ref_multiplier_0_gate_E and load_curr_source_ref_multiplier_0_drain_E using c_route with extension=3 +# connect stage1 to stage2 pmos gate +route between mydiff_tr_multiplier_0_drain_W and load_pfet_right_multiplier_0_gate_con_S using L_route +route between load_pfet_right_multiplier_0_drain_con_S and load_curr_source_mirr_multiplier_0_drain_W using L_route +create a tuple parameter called mim_cap_size +create an int parameter called mim_cap_rows +place a mimcap array called load_miller_cap with rows mim_cap_rows columns 2 size mim_cap_size +# move load_miller_cap by 45,20 +move load_miller_cap to right of mydiff +move load_miller_cap to north of mydiff +move load_miller_cap to right of load_pfet_right +route between mydiffload_top_B_0_drain_N and load_miller_cap_row0_col0_top_met_N using c_route with extension=15 +# route between load_pfet_right_multiplier_0_drain_E and load_miller_cap_row0_col0_top_met_W using straight_route +route between load_pfet_right_multiplier_0_drain_con_S and load_miller_cap_row0_col0_top_met_S using c_route with width1=1.2 width2=1.2 diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/routetest.convo b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/routetest.convo new file mode 100644 index 000000000..8f1be9126 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/convos/routetest.convo @@ -0,0 +1,5 @@ +tester +place an nmos called ref with width 5 length 1 with_dummy False with_tie False with_substrate_tap False with_dnwell False +place an nmos called mirror with width 10 length 1 with_dummy False with_tie False with_substrate_tap False with_dnwell False +move ref to the right of mirror +route between ref_multiplier_0_source_E and mirror_multiplier_0_source_W using straight diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/BiasVoltageGenerator_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/BiasVoltageGenerator_chetanya.txt new file mode 100644 index 000000000..e1eb7a77f --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/BiasVoltageGenerator_chetanya.txt @@ -0,0 +1 @@ +Create a bias voltage generator with parameterised nmos and pmos width, routing widths and fingers. Turn off the substrate taprings for the MOSFETs. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CMbasic_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CMbasic_chetanya.txt new file mode 100644 index 000000000..cae8a32e4 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CMbasic_chetanya.txt @@ -0,0 +1 @@ +place a basic nfet current mirror with parameterised width and multipliers. Route the ties of the fets together. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CMbasic_ver2_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CMbasic_ver2_chetanya.txt new file mode 100644 index 000000000..488dede5d --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CMbasic_ver2_chetanya.txt @@ -0,0 +1 @@ +place an nfet current mirror with width and multipliers as parameters. Route the tie taprings of the fets together. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CascodeCommonGate1_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CascodeCommonGate1_chetanya.txt new file mode 100644 index 000000000..3f9d629b9 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CascodeCommonGate1_chetanya.txt @@ -0,0 +1 @@ +place a cascode common gate stage/poor man's cascode using nfets with parameterised fet width. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CascodeCommonSource_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CascodeCommonSource_chetanya.txt new file mode 100644 index 000000000..ddb250ae6 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CascodeCommonSource_chetanya.txt @@ -0,0 +1 @@ +place an nfet cascode stage with fet width, multipliers, and fingers as parameters and substrate tap and deep n-well turned off. Route the top and bottom right edges of the ties together. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/ClassBPushPull_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/ClassBPushPull_chetanya.txt new file mode 100644 index 000000000..54e2a0075 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/ClassBPushPull_chetanya.txt @@ -0,0 +1 @@ +create a class B power amplifier/Push-Pull amplifer with fet width, fingers, multipliers as paramaters. Turn the nfet's deep-nwell off and do not place substrate taprings for either fet. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier1_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier1_chetanya.txt new file mode 100644 index 000000000..8d86bfb70 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier1_chetanya.txt @@ -0,0 +1 @@ +place an nfet common source amplifier/CS amplifier with the load fet in the diode connected configuration. Parameterise the widths, multipliers, and fingers of the mosfets and keep the substrate taprings off. Do not add deep-nwells to either fet. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier2_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier2_chetanya.txt new file mode 100644 index 000000000..30404ce4c --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier2_chetanya.txt @@ -0,0 +1 @@ +TODO \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier_chetanya.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier_chetanya.txt new file mode 100644 index 000000000..8d6308787 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/CommonSourceAmplifier_chetanya.txt @@ -0,0 +1,2 @@ +place a CS amp/Common Source Amplifier with nfet and pfet width, fingers and routing multipliers as parameters. Turn off the deep nwell and substrate taprings +for the fets. \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/currentmirror.txt b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/currentmirror.txt new file mode 100644 index 000000000..03dc25614 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm/currentmirror.txt @@ -0,0 +1 @@ +make a currentmirror. Make the width, length, num fingers and multipliers all parameters and they should be the same for both the reference and mirror. diff --git a/openfasoc/generators/glayout/glayout/llm/syntax_data/llm_arlene.json b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm_arlene.json new file mode 100644 index 000000000..7793f020a --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/syntax_data/llm_arlene.json @@ -0,0 +1,140 @@ +{ + "data": [ + { + "NLPfilename": "BiasVoltageGenerator", + "LLMprompt": "create a biased voltage generator using an nfet and pfet called m1 and m2 with width, multiplier, and fingers as parameters. Use common centroid technique" + }, + + { + "NLPfilename": "CascodeCommonGate ", + "LLMprompt": "create a common cascode gate using two nfets called m1 and m2 with common centroid technique. make the width a parameter." + }, + + { + "NLPfilename": "ClassBPushPull", + "LLMprompt": "make a class b pull with an nfet and a pfet called m1 and m2 respectively. make the width, fingers, and multipliers parameters and use common centroid technique." + }, + + { + "NLPfilename": "CMbasic", + "LLMprompt": "make a basic current mirror with 2 nmos called m1 and m2 with the parameters width and multiplier" + }, + + { + "NLPfilename": "BiasVGen", + "LLMprompt": "create a biased voltage generator using an nfet and pfet called m1 and m2 with width and multiplier as parameters. Use common centroid technique" + }, + + { + "NLPfilename": "CascodeCommonGate1", + "LLMprompt": "using two nmos, create a common cascode gate with width as a parameter. route the source of 1 to the drain of the other" + }, + + { + "NLPfilename": "cmirror_absmove", + "LLMprompt": "place 2 nfets, 1 with width 5 and the other with width 10 and place the 2nd one at (5,5)" + }, + + { + "NLPfilename": "CommonSourceAmplifier", + "LLMprompt": "Place an nfet and pfet to create a common source amplifier. make the width, multipliers, and fingers parameters. Connect the drain of the nfet to the source of the pfet" + }, + + { + "NLPfilename": "CommonSourceAmplifier1", + "LLMprompt": "Create a common source amplifier using two nmos. Make width, multipliers and fingers parameters and set substrate tap and dnwell to false" + }, + + { + "NLPfilename": "CommonSourceAmplifier2", + "LLMprompt": "With width, multipliers, and fingers as parameter, make a common source amplifier using 2 nfets with 4 routes" + }, + + { + "NLPfilename": "ConstBiasVoltageGen", + "LLMprompt": "Make a constant bias voltage generator with 2 nfets. make the width fingers and rmult as parameters. connect the two nfets using 3 routes" + }, + + { + "NLPfilename": "CrossCoupledInverters", + "LLMprompt": "Using interdigitized technique, create cross coupled inverters using 4 transistors with the top row being pfets and the bottom row being nfets" + }, + + { + "NLPfilename": "CurrentMirror", + "LLMprompt": "Make a current mirror with 2 nfets with parameters width and rmult." + }, + + { + "NLPfilename": "DegenCommonGate", + "LLMprompt": "Make a degen common gate with 2 nfets with parameters width, rmult, and fingers. route with a c route and straight route" + }, + + { + "NLPfilename": "DegenCommonSource", + "LLMprompt": "Make a degen common source with 2 nfets with the parameters width, rmult, and fingers for each. route the nfets and the ties" + }, + + { + "NLPfilename": "DiffPair", + "LLMprompt": "Make a diffpair with parameters, width, fingers, and rmult for 2 nfets. Set the length to 10" + }, + + { + "NLPfilename": "LowNoiseAmp", + "LLMprompt": "make a low noise amplifiers using 2 nfets with the width fingers and rmult as parameters." + }, + + { + "NLPfilename": "NoiseXDiffConv", + "LLMprompt": "make a noise x diff using 2 nfets with parameters width, fingers, and rmult as parameters. route the gate of 1 to the source of the other and route the ties" + }, + + { + "NLPfilename": "opamp", + "LLMprompt": "make an opamp with parameters load_pfet_width, load_pfet_fingers, load_pfet_multipliers and load_curr_source_fingers, load_curr_source_width_ref, load_curr_source_width_mirr, mim_cap_size, and mim_cap_rows. Use common centroid tecnique" + }, + + { + "NLPfilename": "PTATVoltageGen", + "LLMprompt": "create a PTAT voltage generator using 2 nfets with the parameters of fingers, rmult, and width for each. route bewteen the two with 3 routes" + }, + + { + "NLPfilename": "SourceFollow", + "LLMprompt": "make a source follower by connecting 2 nfets with the parameters width and rmult and route between the two from source of 1 to drain of the other" + }, + + { + "NLPfilename": "StrongArmLatch", + "LLMprompt": "make a strong arm latch by importing cross coupled inverters and setting its number of fingers to 4. Use a diffpair, 2 nfets, and 2 pfets " + }, + + { + "NLPfilename": "CTATVGen", + "LLMprompt": "make a ctat voltage generator with 2 nfets. Make m1 and m2 (the nfets) have parameters width and rmult." + }, + + { + "NLPfilename": "RegCascode", + "LLMprompt": "Make a regulated cascode with 2 nfets that have width and rmult as parameters. Route the two nfets with 2 routes" + }, + + { + "NLPfilename": "ULPD", + "LLMprompt": "Create an ultra low powered diode with 2 nfets each with parameters width, multipliers and fingers. Use 2 routes" + }, + + { + "NLPfilename": "Varactor", + "LLMprompt": "make a variable reactance diode with width and multiplier as variables. make 3 routes with common centroid technique" + }, + + { + "NLPfilename": "VoltageFollower", + "LLMprompt": "using common centroid technique, make a voltage follower with width and multiplier as parameters. Use two routes to connect two nfets together" + } + + ] +} + diff --git a/openfasoc/generators/glayout/glayout/llm/train.py b/openfasoc/generators/glayout/glayout/llm/train.py new file mode 100644 index 000000000..7a42bb0ee --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/train.py @@ -0,0 +1,221 @@ +import requests +from pathlib import Path + +from glayout.llm.manage_data import load_all_labeled_syntax_data_json, RAGdb +# from manage_data import load_all_labeled_syntax_data_json, RAGvecdb, get_glayout_context +import torch +import time +from peft import get_peft_config, get_peft_model, LoraConfig +from datasets import Dataset + +# from transformers import AutoModelForCausalLM, AutoTokenizer, Conv1D, TrainingArguments +from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments +import transformers + +def get_prompt_from_template( + glayout_NLPcontext: str, ragcontext: str, prompt: str, instruct: bool = False +) -> str: + prompt = f""" +[CONTEXT] +[EXPLAINING STRICT SYNTAX] +Below is some context on Glayout strictsyntax: +{glayout_NLPcontext} +[/EXPLAINING STRICT SYNTAX] +Below is context on analog circuit design which will help you convert an example prompt to Glayout strictsyntax +{ragcontext} +[/CONTEXT] +---- +[TRANSLATION_TASK] +Convert the following prompt to Glayout strictsyntax: +{prompt} +[/TRANSLATION_TASK] +""" + if instruct: + prompt = f"[INST] {prompt} [/INST]" + return prompt + + +# pass all prompts through rag before handing training data to llm +def add_context_to_data(data: list) -> list: + """Enhance a list of data pairs (prompt and result) with contextual information from external documents. + This function takes each prompt-result pair in the input data, queries an vector database for relevant documents, + constructs a new prompt incorporating this contextual information according to a specified template, and returns the modified + list of prompt-result pairs with added context. + + Args: + data (list): A list of tuples, where each tuple is (prompt (str), result (str)) + + Returns: + list: same format as input but the prompt has additional context and is correctly formated + """ + glayout_context = get_glayout_context() + contextualized_prompts = list() + for prompt, result in data: + docs = RAGvecdb.query(prompt, 2) + ragdata = str() + for i, doc in enumerate(docs): + ragdata += f"[CONTEXT DOCUMENT NUMBER {i}]\n" + ragdata += doc + "\n" + ragdata += f"[/CONTEXT DOCUMENT NUMBER {i}]\n" + newprompt = get_prompt_from_template(glayout_context, ragdata, prompt) + contextualized_prompts.append((newprompt, result)) + return contextualized_prompts + + +# credit +# https://stackoverflow.com/questions/76768226/target-modules-for-applying-peft-lora-on-different-models +def get_lora_supported_layer_names(model) -> list: + """create a list of lora supported layers in a particular model + This allows for easy setting of layers when creating a lora config + only use this if you want to use lora with all the layers + + Args: + model (huggingface model): any model compatible with hugging face + + Returns: + list: list of strings (layer names) + """ + # Create a list to store the layer names + layer_names = list() + # Recursively visit all modules and submodules + for name, module in model.named_modules(): + # Check if the module is an instance of the specified layers + # if isinstance(module, (torch.nn.Linear, torch.nn.Embedding, torch.nn.Conv2d, Conv1D)): + if isinstance(module, (torch.nn.Linear, torch.nn.Embedding, torch.nn.Conv2d)): + layer_names.append(".".join(name.split(".")[4:]).split(".")[0]) + layer_names = list(set(layer_names)) + return [name for name in layer_names if not (name.isspace() or len(name) == 0)] + + +# returns model, tokenizer +def load_model_and_tokenizer(device: str, lora: bool = True) -> tuple: + """Downloads or restores model and tokenizer + converts the model to half precision + moves tokenizer and model to the specified device + + Args: + device (str): move model to device (tokenizer always runs on CPU) + (e.g., 'cpu', 'cuda'). + + Returns: + tuple: first element is model and second is tokenizer. + + Raises: + ValueError: If there is an error in loading the model or tokenizer. + RuntimeError: If there is an error moving the model to the specified device. + """ + accesstoken = "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" + modelname = "mistralai/Mistral-7B-v0.1" + modelname = "mistralai/Mistral-7B-Instruct-v0.2" + model = AutoModelForCausalLM.from_pretrained(modelname, token=accesstoken) + tokenizer = AutoTokenizer.from_pretrained(modelname, use_fast=True) + if lora: + peft_config = LoraConfig( + task_type="CAUSAL_LM", + r=8, + lora_alpha=32, + lora_dropout=0.1, + target_modules=get_lora_supported_layer_names(model), + ) + model = get_peft_model(model, peft_config) + model.print_trainable_parameters() + model.half() + model.to(device) + tokenizer.pad_token = tokenizer.eos_token + return model, tokenizer + + +def run_llm_normal( + model, tokenizer, device: str, prompt: str, max_new_tokens: int = 500 +) -> str: + """Generate a text completion for a given prompt using a provided language model. + + Args: + model: The language model to use, should be compatible with huggingfaceinterface + device (str): The device where the model is currently located + prompt (str): The initial text to prompt the language model with. + max_new_tokens (int, optional): maximum number of new tokens to generate. Defaults to 500. + + Returns: + str: The text generated by the language model as a continuation of the prompt. + """ + model.eval() + inputs = tokenizer(prompt, return_tensors="pt") + inputs.to(device) + outputs = model.generate(**inputs, max_new_tokens=max_new_tokens) + return tokenizer.decode(outputs[0], skip_special_tokens=True) + + +# tokenize both the prompts and expected responses +def pre_tokenize_and_convert_dataset_to_arrow(tokenizer, data: list) -> list: + tokenized_prompts = list() + tokenized_responses = list() + for prompt, response in data: + tokenized_prompt = tokenizer(prompt, return_tensors="pt") + tokenized_response = tokenizer(response, return_tensors="pt") + tokenized_prompts.append(tokenized_prompt) + tokenized_responses.append(tokenized_response) + dictionary_data = {"prompt": tokenized_prompts, "strictsyntax": tokenized_responses} + return tokenized_data + + +def train(model, tokenizer, data): + data = pre_tokenize_dataset(tokenizer, data) + model.train() + # hyperparameters + lr = 2e-4 + batch_size = 4 + num_epochs = 10 + # define training arguments + training_args = TrainingArguments( + output_dir="glayout_llm_checkpoints", + learning_rate=lr, + per_device_train_batch_size=batch_size, + per_device_eval_batch_size=batch_size, + num_train_epochs=num_epochs, + weight_decay=0.01, + logging_strategy="epoch", + evaluation_strategy="epoch", + save_strategy="epoch", + load_best_model_at_end=True, + gradient_accumulation_steps=4, + warmup_steps=2, + fp16=True, + # optim="paged_adamw_8bit", + ) + # configure trainer + trainer = transformers.Trainer( + model=model, + train_dataset=tokenized_data["train"], + eval_dataset=tokenized_data["test"], + args=training_args, + data_collator=data_collator, + ) + # train model + model.config.use_cache = False # silence warnings + trainer.train() + model.config.use_cache = True # reenable warnings + return model + + +def run_full_training(): + device = "cuda" if torch.cuda.is_available() else "cpu" + model, tokenizer = load_model_and_tokenizer(device) + # load fine tuning data + labeled_data = add_context_to_data(load_all_labeled_syntax_data_json()) + return train(model, tokenizer, labeled_data) + + +run_full_training() +# # time was 28.69 seconds for CPU +# # time on GPU including transfering to and from GPU 19.33 seconds +# # raw GPU compute time 1.31 seconds +# # time if you do not move the model back to the cpu 9.67 seconds + +# text = "Hello my name is" + +# print(run_llm_normal(model, tokenizer, DEVICE, text)) + + +# data["train"] and data["test"] +# each is \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/llm/validate_synthetic_data.py b/openfasoc/generators/glayout/glayout/llm/validate_synthetic_data.py new file mode 100644 index 000000000..12d42fa93 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/llm/validate_synthetic_data.py @@ -0,0 +1,50 @@ +# Utilities included in this python file can be used to +# 1- verify the test_cases +# 2- augment test_cases to synthetically increase the number of data points + + +import os +import traceback +from pathlib import Path +from typing import Union + +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk +import glayout.syntaxer.dynamic_load + + +def instantiate_convo(pdk: MappedPDK, convo_file: Union[str, Path]) -> bool: + """Instantiates a layout for the given conversation file + + Args: + convo_file (str or Path): path to Glayout .convo file + + Returns: + bool: True if the cell was instantiated + """ + try: + # convert NLP to code and pass to show_glayout_code_cell + session_code = glayout.syntaxer.dynamic_load.run_session(load_conversation=convo_file, restore_and_exit=True) + return glayout.syntaxer.dynamic_load.show_glayout_code_cell(pdk, session_code) + except Exception as e: + print(f"Error running session with {convo_file}: {e}") + print(traceback.format_exc()) + return False + + +def run_all_tests(test_cases_dir: Union[str, Path] = "./syntax_data/convos"): + """Run all test cases found in the 'test_cases' directory.""" + # Directory containing test cases + test_cases_dir = str(Path(test_cases_dir).resolve()) + # Get all files in the directory that end with ".convo" + convo_files = [f for f in os.listdir(test_cases_dir) if f.endswith(".convo")] + # run and verify all convos + for convo_file in convo_files: + convo_file_path = os.path.join(test_cases_dir, convo_file) + print(f"Running session with {convo_file_path}") + instantiate_convo(pdk=sky130_mapped_pdk, convo_file=convo_file_path) + + +if __name__ == "__main__": + run_all_tests() + diff --git a/openfasoc/generators/glayout/glayout/syntaxer/__init__.py b/openfasoc/generators/glayout/glayout/syntaxer/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/syntaxer/__init__.py @@ -0,0 +1 @@ + diff --git a/openfasoc/generators/glayout/glayout/syntaxer/dynamic_load.py b/openfasoc/generators/glayout/glayout/syntaxer/dynamic_load.py new file mode 100644 index 000000000..df124a2c1 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/syntaxer/dynamic_load.py @@ -0,0 +1,205 @@ +# this file contains functions which dynamically create and import cells, and run sessions +import importlib.util +import inspect +import os +import re +from typing import Callable, Union +from pathlib import Path +import tempfile +import glayout.syntaxer.relational + +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.util.port_utils import PortTree +import sys +import traceback +import glayout.syntaxer.process_input + + +def reload_saved_convo(convo_file: Union[Path, str]) -> tuple: + """restores a conversation from a .convo file + + Args: + convo_file (Union[Path, str]): path to the .convo file + + Returns tuple: + Session: restored conversation object + loop_count: prompt index after restoring the conversation + """ + savedconvo = Path(convo_file).resolve() + if not savedconvo.is_file(): + raise FileNotFoundError("load conversation should be from an existing file") + with savedconvo.open("r") as loadconvo: + lines = loadconvo.readlines() + for i, line in enumerate(lines): + if i == 0: + convo = glayout.syntaxer.process_input.Session(inputstream=sys.stdin, outputstream=sys.stdout, toplvlname=line) + continue + convo.process_next_input(line) + loop_count = len(lines) - 1 + print("\n\nloaded conversation from " + str(savedconvo)) + return convo, loop_count + + +def run_session(load_conversation: Union[str, Path], restore_and_exit: bool=False) -> str: + """Manage, interact, and run conversation sessions from command line + + Returns: + str: Glayout python code corresponding to the current session + """ + # start convo and intialize loop counter (will be updated if we read from file) + # if saved convo then load everything from save file, else read form stdio + if load_conversation is not None: + convo, loop_count = reload_saved_convo(load_conversation) + if restore_and_exit: + return convo.code.get_code() + else: + convo, loop_count = glayout.syntaxer.process_input.Session(inputstream=sys.stdin, outputstream=sys.stdout), int(0) + # enter design loop + session_ongoing = True + while session_ongoing: + convo.print_to_stream("\ntask " + str(loop_count)) + loop_count += 1 + convo.print_to_stream(convo.generic_prompt) + try: + session_ongoing = convo.process_next_input(convo.read_from_stream()) + except Exception as e: + print(traceback.format_exc()) + print("an exception was encounterd") + print(str(e)) + print("restoring last valid state and resuming regular program execution\n") + convo = convo.backup + loop_count -= 1 + session_ongoing = True + return convo.code.get_code() + + + +def get_default_arguments(func: Callable, pdk: MappedPDK) -> dict: + """Gets default arguments to a function based on its argument types. + + Args: + func (callable): The function to which default arguments will be added. + pdk (MappedPDK): If one of the non default args is of type MappedPDK, then this pdk is used for default value + + Returns: + dict: A dictionary containing default arguments with values based on the argument types of the input function. + """ + # get args that dont have a default + argspec = inspect.getfullargspec(func) + args_with_defaults = argspec.defaults or [] + num_args_without_defaults = len(argspec.args) - len(args_with_defaults) + args_without_defaults = argspec.args[:num_args_without_defaults] + # loop through non default args and try to set some value for them + kwargs = {} + for arg, arg_type in zip(args_without_defaults, argspec.annotations.values()): + # pick some default value + if arg_type == int: + default_value = 2 + elif arg_type == float: + default_value = 2.5 + elif arg_type == bool: + default_value = True + elif arg_type == str: + default_value = "met1" + # hard to guess what a person might want with a tuple, but make it a pair of ints (size) is a safe bet + elif arg_type == tuple: + default_value = (1,1) + # hard to guess what a person might want with a list, but make it a long list of ints is a safe bet + elif arg_type == list: + default_value = [1,1,1,1,1,1,1,1,1] + elif arg_type == MappedPDK: + default_value = pdk + else: # for other types, set default to None + default_value = None + # add this argument to the kwargs + kwargs[arg] = default_value + return kwargs + + +def get_funccell_name(glayout_code: str) -> str: + pattern = r"def\s+([a-zA-Z_][a-zA-Z0-9_]*)cell\s*\(" + return re.search(pattern, glayout_code).group().lstrip("def").rstrip("(").strip() + +class CodeImportHandler: + """create, manage, destroy temporary files created as part of dynamic importing + contains + self.function (Callable): the function handle + self.func_name (str): the name of the function imported + self.temp_module: the imported module handle + """ + + def __init__(self, glayout_code: Union[str, Path]): + """create temporary file with glayout python code from glayout_code string + and import the module + Args: + glayout_code (str, Path): string containing cell function and imports. + ****or, string or pathlib.Path: path to a convo file + """ + # check if this is a code string or a file + precompiled_code = False + if isinstance(glayout_code, str): + if glayout.syntaxer.relational.GlayoutCode.HEAD_MARKER in glayout_code: + precompiled_code = True + # if this is not a code string, convert NLP file to python code + if not precompiled_code: + glayout_code = run_session(glayout_code, restore_and_exit=True) + # figure out what the cell is called + self.func_name = get_funccell_name(glayout_code) + pymodule_name = self.func_name.removesuffix("_cell") + ".py" + # create the cell py module + with tempfile.TemporaryDirectory() as temp_dir: + temp_dir_path = Path(temp_dir).resolve() + pythonfile = temp_dir_path / pymodule_name + with pythonfile.open(mode="w") as pyfile: + pyfile.write(glayout_code) + # import the cell + spec = importlib.util.spec_from_file_location(self.func_name, pythonfile) + self.temp_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(self.temp_module) + self.function = getattr(self.temp_module, self.func_name) + + + +def run_glayout_code_cell(pdk: MappedPDK, glayout_code: str) -> bool: + """Instantiate a layout from the given glayout cell code and returns Component + + Args: + pdk (MappedPDK): pdk to instantiate this cell + glayout_code (str): string containg the cell function and all needed imports + + Returns: + Component: gdsfactory Component corresponding to the produced layout + """ + testcell = CodeImportHandler(glayout_code).function + return testcell(**get_default_arguments(testcell, pdk)) + + +def show_glayout_code_cell(pdk: MappedPDK, glayout_code: str): + """Instantiate and show a layout from the given glayout cell code + + Args: + pdk (MappedPDK): pdk to instantiate this cell + glayout_code (str): string containg the cell function and all needed imports + """ + run_glayout_code_cell(pdk, glayout_code).show() + + +def getPortTree_glayout_code_cell(pdk: MappedPDK, glayout_code: str) -> PortTree: + """return PortTree for a given glayout cell + + Args: + pdk (MappedPDK): pdk to instantiate this cell + glayout_code (str): string containg the cell function and all needed imports + + Returns PortTree + """ + return PortTree(run_glayout_code_cell(pdk, glayout_code)) + +def printPortTree_glayout_code_cell(pdk: MappedPDK, glayout_code: str): + """return PortTree for a given glayout cell + + Args: + pdk (MappedPDK): pdk to instantiate this cell + glayout_code (str): string containg the cell function and all needed imports + """ + PortTree(run_glayout_code_cell(pdk, glayout_code)).print(depth=6) diff --git a/openfasoc/generators/glayout/glayout/syntaxer/nltk_init_deps.py b/openfasoc/generators/glayout/glayout/syntaxer/nltk_init_deps.py new file mode 100644 index 000000000..7e73950c8 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/syntaxer/nltk_init_deps.py @@ -0,0 +1,19 @@ +import nltk + + +def check_and_download_nltk_data(data_name): + """ + Check if an NLTK dataset is downloaded, and download it if it is not. + + Args: + data_name (str): The name of the NLTK dataset to check and download. + """ + try: + nltk.data.find(f"tokenizers/{data_name}") + except LookupError: + print(f"{data_name} is not downloaded. Downloading now...") + nltk.download(data_name) + print(f"{data_name} has been downloaded.") + + +check_and_download_nltk_data("punkt") diff --git a/openfasoc/generators/glayout/glayout/syntaxer/process_input.py b/openfasoc/generators/glayout/glayout/syntaxer/process_input.py new file mode 100644 index 000000000..f21eefae2 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/syntaxer/process_input.py @@ -0,0 +1,390 @@ +import copy +import io +from pathlib import Path +from typing import Optional, Union + +import nltk +import glayout.syntaxer.nltk_init_deps +import glayout.syntaxer.dynamic_load +from glayout.syntaxer.relational import GlayoutCode, parse_direction +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk + +class Session: + """The session stores all relevant information for producing code from a conversation""" + + generic_prompt = """Place a cell, move a cell, route, create a parameter, or define a variable. +You can also dump code or save this conversation, or enter "help" to see supported syntax in detail +What would you like to do?""" + + def __init__( + self, + outputstream: io.IOBase, + inputstream: Optional[io.IOBase] = None, + toplvlname: Optional[str] = None, + ): + """initialize a conversation and greet the user + Args: + outputstream (io.IOBase): used to print outputs + inputstream (io.IOBase): saved for (optionally) reading in user input, also just provide a string + NOTE: if input stream not provided, str input must be provided + toplvlname (str): in string only mode, you can input toplvl name using this arg + """ + self.inputstream = inputstream + self.outputstream = outputstream + # greet the user and get a top level name for the component + if toplvlname is None: + if inputstream is None: + raise RuntimeError( + "you must specify AT LEAST one of inputstream or name" + ) + self.print_to_stream("Hello!") + self.print_to_stream( + "Please provide a name for the Component you want to create" + ) + self.print_to_stream( + "remember, this will be the name of your top level component: " + ) + self.name = self.read_from_stream().strip() + else: + self.name = str(toplvlname).strip() + # save a list of responses to recreate this component from a .conv file + self.conversation_responses = list() + self.conversation_responses.append(self.name) + # init the code object + self.code = GlayoutCode(self.name) + # create a backup that goes back exactly one call to process_next_input + self.backup = self.__backup() + + def __backup(self): + """Produce an exact copy of this class to revert after an exception""" + newobj = self.__class__ + backup = newobj.__new__(newobj) + backup.inputstream = self.inputstream + backup.outputstream = self.outputstream + backup.conversation_responses = copy.deepcopy(self.conversation_responses) + backup.code = copy.deepcopy(self.code) + backup.backup = None + return backup + + def print_help(self): + """print help message to self.outputstream""" + syntaxes = list() + # place_syntax + syntaxes.append("place an genidhere called/named compnamehere with paramshere") + # route_syntax + syntaxes.append("route between/from port1 and/to port2 using routetype with paramshere") + # absolute move syntax + syntaxes.append("move compname [by] (x,y)") + # relative move + syntaxes.append("move compname [filler words] direction [filler words] reference_comp [by separation]") + # import + import_syntax = "import comp1, comp2 from mod1, and comp3 from some/path/mod.py" + import_syntax += "\n\tNOTE: imports from glayout only need component name" + import_syntax += "\n\tNOTE: if mod name is not specified, it is assumed to be the component name" + syntaxes.append(import_syntax) + # parameter + syntaxes.append("create/define [a/an] param_type parameter called/named paramname") + # variable + syntaxes.append("create/define [a/an] var_type variable called/named varname =/equal valorexpr") + syntaxes.append("save/dump [conversation/code] to pathtosaveto\n\tNOTE: save code is assumed if neither conversation nor code are specified") + syntaxes.append("show\n\tNOTE: This will show the current component in klayout using .show and klive plugin") + # print all + self.print_to_stream("\nBelow are valid sentences:") + for syntax in syntaxes: + self.print_to_stream(syntax) + self.print_to_stream() + + def save_to_disk( + self, savecode: bool = True, savepath: Optional[Union[str, Path]] = None + ): + """Save NLP results to disk, either save code or conversation responses. + Args: + savemode: bool, default to True==save code, false==save conversation responses + savepath: Optional (if None, defaults to ./), + NOTE: None is the same as specifying the current directory "./" + """ + # figure out which save mode we are in + suffix = ".py" if savecode else ".convo" + if savepath is None: + savepath = Path("./").resolve() + savepath = Path(savepath.strip()) if isinstance(savepath, str) else savepath + if savepath.is_dir(): + savepath /= f"{self.code.toplvl_name}{suffix}" + if savepath.parent.is_dir(): + with savepath.open("w") as savefile: + if savecode: + savefile.write(self.code.get_code()) + else: + savefile.writelines([r + "\n" for r in self.conversation_responses]) + else: + raise FileNotFoundError("the directory you specfied does not exist") + + def print_to_stream(self, toprint: Optional[str] = None): + """prints to the configured outputstream + + Args: + toprint (Optional[str]): string to print. If None then prints a newline. + """ + if toprint is None: + toprint = "" + self.outputstream.write(toprint + "\n") + + def read_from_stream(self) -> str: + """reads user input from the configured inputstream + + Returns: + str: the next line of user input + """ + response = self.inputstream.readline() + # self.conversation_responses.append(response) + return response + + def __save_response(self, response: str): + self.conversation_responses.append(response.strip()) + + def process_import_sentence(self, text_input: str) -> bool: + """Will update the internal code table using a sentence which follows the import syntax + + Args: + text_input (str): user input text + + Returns: + bool: saveresponse + """ + words = nltk.word_tokenize(text_input) + imports = text_input.replace("and", ",").replace("import", "").replace("from", "").strip().split(",") + for modimport in imports: + if modimport == "" or modimport.isspace(): + continue + words = modimport.strip().split() + compname = words[0] + modpath = words[1] if len(words) > 1 else None + aliases = [compname] # TODO implement comp aliasing + self.code.update_import_table(aliases, compname, modpath) + return True + + def process_param_or_var_sentence(self, text_input: str) -> bool: + """Will update the internal code table using a sentence which follows the variable or parameter define syntax + + Args: + text_input (str): user input text + + Returns: + bool: saveresponse + """ + words = nltk.word_tokenize(text_input) + vartype = None + for word in words: + word = word.lower().strip() + if "int" in word: + vartype = int + elif "float" in word: + vartype = float + elif "bool" in word: + vartype = bool + elif "str" in word: + vartype = str + elif "tupl" in word: + vartype = tuple + if vartype is not None: + break + varname = None + for i, word in enumerate(words): + if word in ["called", "named"]: + varname = words[i + 1] + break + expr = None + eqpar = text_input.replace("equal", "=").strip().removesuffix(".") + expr = eqpar.split("=")[1] if "=" in eqpar else None + if "parameter" in words: + self.code.update_parameter_table(varname, vartype, None, None) + else: # variable + self.code.update_variable_table(varname, expr) + return True + + def process_place_sentence(self, text_input: str) -> bool: + """Will update the internal code table using a sentence which follows the place syntax + + Args: + text_input (str): user input text + + Returns: + bool: saveresponse + """ + words = text_input.strip().split()#nltk.word_tokenize(text_input) + if "configuration" in text_input: + raise NotImplementedError("configs not yet implemented") + # util func + def split_input_on_compname(text_input:str) -> str: + """Split a string on the words 'called' or 'named' and retain everything before these keywords. + Args: + text_input (str): The input string. + Returns: + str: The part of the text_input before 'called' or 'named'. + """ + # look for either named or called in a lower case version of parts + lc_parts = text_input.lower().split() + indexCalled, indexNamed = None, None + if "called" in lc_parts: + indexCalled = lc_parts.index("called") + if "named" in lc_parts: + indexNamed = lc_parts.index("named") + # pick which index to use or raise error if neither + index = None + if (indexCalled is not None) and (indexNamed is not None): + index = max(indexCalled,indexNamed) + else: + index = indexCalled if (indexCalled is not None) else indexNamed + if index is None: + raise SyntaxError("invalid place syntax, place sentence must include 'called' or 'named' keyword") + # return everything in the sentence before the "called" or "named" keyword + parts = text_input.split() + return " ".join(parts[0:index]) + genid = self.code.find_first_generator_id(split_input_on_compname(text_input)) + comp_name = None + for i, word in enumerate(words): + if word in ["called", "named"]: + comp_name = words[i + 1] + break + params = None + for i, word in enumerate(words): + if word == "with": + params = " ".join(words[i:]) + break + self.code.update_place_table(genid, params, comp_name) + return True + + def process_route_sentence(self, text_input: str) -> bool: + """Will update the internal code table using a sentence which follows the route syntax + + Args: + text_input (str): user input text + + Returns: + bool: saveresponse + """ + words = nltk.word_tokenize(text_input) + port1 = None + port2 = None + routetype = None + params = None + for i, word in enumerate(words): + if word in ["between", "from"]: + port1 = words[i + 1] if port1 is None else port1 + elif word in ["from", "and"]: + port2 = words[i + 1] if port2 is None else port2 + elif word in ["using", "a", "an"]: + routetype = words[i + 1] + elif word == "with": + params = " ".join(words[i:]) + self.code.update_route_table(port1, port2, params, routetype) + return True + + def process_move_sentence(self, text_input: str) -> bool: + """Will update the internal code table using a sentence which follows the route syntax + + Args: + text_input (str): user input text + + Returns: + bool: saveresponse + """ + words = nltk.word_tokenize(text_input) + direction = None + reference_comp = None + separation = None + # words[0] is "move" and words[1] is the component name + for i, word in enumerate(words[2:]): + # try to find direction until we see it then move on + if parse_direction(word) is not None and direction is None: + direction = word + # once you see a direction, look for a reference_comp + elif (word=="by" or word=="with") and reference_comp is None: + reference_comp = words[2:][i-1] + separation = words[2:][i+1] + if len(separation)>3 and separation.strip().lower()[0:3]=="sep": + separation = words[2:][i+2] + # you may not find it in the loop, in which case reference_comp is the last word + if reference_comp is None: + reference_comp = words[-1] + # relative move + if direction is not None: + spacesepwords = text_input.split() + self.code.update_move_table("relative", words[1], reference_comp, direction, separation) + else: # assume absolute move + words = nltk.word_tokenize( + text_input.replace("(", "").replace(")", "").replace("by", "").replace("with","") + ) + self.code.update_move_table("absolute", words[1], words[2]) + return True + + def show_current_component(self, text_input: str) -> False: + """displays the current state of the layout with klayout using .show in sky130nm tech + if the keyword "port" is found within text_input, port tree (with depth 6) will be saved to a file instead + Args: + text_input (str): user input text + Returns: + False: saveresponse=False + """ + if "port" in text_input.lower(): + glayout.syntaxer.dynamic_load.printPortTree_glayout_code_cell(sky130_mapped_pdk,self.code.get_code()) + elif "param" in text_input.lower(): + print(*self.code.parameter_table,sep="\n") + else: + glayout.syntaxer.dynamic_load.show_glayout_code_cell(sky130_mapped_pdk, self.code.get_code()) + return False + + def process_next_input(self, text_input: str) -> bool: + """main driver for doing things + returns True if session is ongoing + """ + self.backup = self.__backup() + sentences = nltk.sent_tokenize(text_input) + for sentence in sentences: + saveresponse = True + sentence = sentence.strip().removesuffix(".") + words = nltk.word_tokenize(sentence) + mode_indicator = words[0].strip().replace("-", "").lower() + # parse user input + if mode_indicator[0] == "h": # help + saveresponse = False + self.print_help() + elif mode_indicator[0] == "i": # import + saveresponse = self.process_import_sentence(sentence) + elif "create" in mode_indicator or "define" in mode_indicator: # create/define a variable or param + saveresponse = self.process_param_or_var_sentence(sentence) + elif mode_indicator[0] == "p": # place + saveresponse = self.process_place_sentence(sentence) + elif mode_indicator[0] == "r": # route + saveresponse = self.process_route_sentence(sentence) + elif mode_indicator[0] == "m": # move + saveresponse = self.process_move_sentence(sentence) + elif mode_indicator[0] == "e": # dump code and exit + saveresponse = False + self.print_to_stream("\n" + self.code.get_code()) + return False + elif ( + mode_indicator == "save" or mode_indicator == "dump" + ): # save the conversation to a .convo file, or code to .py file + saveresponse = False + savepath = None + for i, word in enumerate(words): + if word in ["to", "at", "path"]: + savepath = "".join(words[i + 1 :]) + break + savecode = any( + wr in words for wr in ["code", "python", "py", "program"] + ) + self.save_to_disk(savecode, savepath) + elif mode_indicator[0] == "s": # show a component + saveresponse = self.show_current_component(sentence) + elif mode_indicator[0] == "#" or mode_indicator[0]=="/": # comment + saveresponse=True + else: + self.print_to_stream("invalid input") + self.print_to_stream("sentences must begin with either place, route, generate, show, dump, or move") + saveresponse = False + # save when needed and return True to continue prompting for input + if saveresponse: + self.__save_response(sentence) + return True diff --git a/openfasoc/generators/glayout/glayout/syntaxer/relational.py b/openfasoc/generators/glayout/glayout/syntaxer/relational.py new file mode 100644 index 000000000..9c613c857 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/syntaxer/relational.py @@ -0,0 +1,887 @@ +import copy +import inspect +import itertools +import operator +import re +import shutil +import string +from abc import ABC, abstractmethod +from importlib import import_module +from pathlib import Path +from typing import Callable, Literal, Optional, Union, cast +import datetime +import glayout.syntaxer.dynamic_load + +import nltk +import glayout.syntaxer.nltk_init_deps + +def list_cartesian_product(list1: list, list2: list, both: bool=False) -> list: + """Compute the Cartesian product of two lists and combine elements into a list of strings. + + Args: + list1 (list): The first list. + list2 (list): The second list. + both (bool): provide the cartesian product of list1,list2 AND list2,list1 + + Returns: + list: A list containing the Cartesian product of the input lists as strings. + """ + # Cartesian product of the two lists + cartesian_product = list(itertools.product(list1, list2)) + # Combine the elements into a list of strings + combined_list = [' '.join(map(str, pair)) for pair in cartesian_product] + return combined_list + (list_cartesian_product(list2,list1,False) if both else []) + + +# used to automatically update supported actions whenever a class which inherits from GlayoutAction is added to this file +def get_all_derived_classes(class_type, remove_parent: bool=True) -> list: + """Finds all derived classes of a particular parent type in the current python file\ + Args: + class_type (any): the parent type you want derived classes for + remove_parent (bool): if True, do not include the parent class in the returned list + Returns list of derived classes + """ + all_classes = [classname for name, classname in globals().items() if isinstance(classname, type)] + glayout_actions = [classname for classname in all_classes if issubclass(classname, GlayoutAction)] + try: + glayout_actions.remove(GlayoutAction) + except ValueError: + pass + return glayout_actions + + +def parse_direction(direction: str) -> Union[str,None]: + """parses a string to see if it contains a valid direction are up/north/above, right/east, left/west, down/south/below + Args: + direction (str) + Returns None if no direction found or an integer 0-3 (0=west,1=north,2=south,3=east) + """ + direction = direction.lower().strip() + findany = lambda strlst : any(ind in direction for ind in strlst) + if findany(["left","west"]): + return 0 + elif findany(["up","north","above"]): + return 1 + elif findany(["right","east"]): + return 2 + elif findany(["down","south","below"]): + return 3 + else: + return None + + + +class ParametersList: + """store function parameter information + self.params is a list with the following format: + [ {name, defaultvalue, type}, ... ] + if niether type nor default is specified, both fields contain inspect.Parameter.empty + self.func_name: str the name of the function + """ + class noprintobj: + def __init__(self,data): + self.data = data + def __str__(self): + return str(self.data) + def __repr__(self): + return str(self.data) + + def __init__(self, func)->dict: + self.func_name = func.__name__ + parameters = inspect.signature(func).parameters + self.params = list() + for param_name, param in parameters.items(): + paraminfo = {"name":param_name, 'defaultvalue':param.default, 'type':param.annotation} + self.params.append(paraminfo) + + def __iter__(self): + return iter(self.params) + def names(self): + return [param["name"] for param in self.params] + def find(self, param_name: str): + """look for param with name param_name. + If found, return info, else return None""" + for param in self.params: + if param["name"]==param_name: + return param + return None + def __str__(self): + return str(self.params) + + def __construct_glayers(self) -> dict: + glayerids = ['met1', 'met2', 'met3', 'met4', 'met5', 'metal1', 'metal2', 'metal3', 'metal4', 'metal5', 'capmet', 'active', 'diffusion', 'tap', 'welltap', 'n+s/d', 'p+s/d', 'polysilicon', 'poly', 'p+', 'n+', 'deep nwell', 'pwell', 'nwell', 'dnwell'] + glayerids += ["metal 1", "metal 2","metal 3","metal 4","metal 5","met 1","met 2","met 3","met 4","met 5"] + glayerids += ["via 1", "via 2","via 3","via 4","via1","via2","via3","via4","metal contact","mcon","metal con"] + glayers = dict() + for glayerid in glayerids: + if "p+" in glayerid: + glayers[glayerid] = "p+s/d" + elif "n+" in glayerid: + glayers[glayerid] = "n+s/d" + elif "poly" in glayerid: + glayers[glayerid] = "poly" + elif "cap" in glayerid: + glayers[glayerid] = "capmet" + elif "tap" in glayerid: + glayers[glayerid] = "active_tap" + elif "active" in glayerid or "diff" in glayerid: + glayers[glayerid] = "active_diff" + elif "well" in glayerid: + if "d" in glayerid: + glayers[glayerid] = "dnwell" + elif "n" in glayerid: + glayers[glayerid] = "nwell" + else: + glayers[glayerid] = "pwell" + elif "via" in glayerid: + for i in range(1,5): + if str(i) in glayerid: + glayers[glayerid] = f"via{i}" + elif "met" in glayerid: + for i in range(1,6): + if str(i) in glayerid: + glayers[glayerid] = f"met{i}" + elif "con" in glayerid: + glayers[glayerid] = "mcon" + else: + raise ValueError("could not parse this id") + return glayers + def __cfgformat_from_list(self, strlist: list[str]): + rtrstr = str() + for ele in strlist: + rtrstr += "\'" + ele + "\' | " + rtrstr =rtrstr.removesuffix("| ") + return rtrstr + def __custom_tokenize(self, sentence: str) -> list: + sentence = sentence.strip().removesuffix(".").removeprefix("with") + # save and replace all instances of strings with indicator words + string_pattern = re.compile(r'\'[^\']*\'|\"[^\"]*\"') + string_list = [str(match.group()) for match in string_pattern.finditer(sentence)] + string_list = [w.replace("\"","").replace("'","").strip() for w in string_list] + sentence = re.sub(string_pattern, "sTRingindICatorWOrd", sentence) + # add a space after every symbol (except period and _) + rsymbols = list(set(string.punctuation) - {'.', '_'}) + parsedsentence = str() + for char in sentence: + if char in rsymbols: + parsedsentence += f" {char} " + else: + parsedsentence += char + sentence = copy.deepcopy(parsedsentence) + # double space (add one space everywhere there is a space) + parsedsentence = str() + for char in sentence: + if char.isspace(): + parsedsentence += f" {char}" + else: + parsedsentence += char + sentence = copy.deepcopy(parsedsentence) + # save and replace all instances of numbers with indicator words + number_pattern = re.compile(r'(?:^|\s|\b)-?\d+(\.\d+)?([eE]-?\d+)?(?=\s|$|[^\w\d])') + number_list = [str(match.group()) for match in number_pattern.finditer(sentence)] + number_list = [num.strip() for num in number_list] + sentence = re.sub(number_pattern, "nUMptindICatorWOrd", sentence) + # save numbers and strings to list and return tokenized sentence + self.__number_list = number_list + self.__string_list = string_list + return sentence.split() + def __get_str_grammar(self, known_paramsORvars: list) -> str: + if len(known_paramsORvars)==0: + known_paramsORvars = ["thiswordwillsurelyneverevercomeupduringparsing"] + basic_grammar = str() + basic_grammar += """S -> ArgList\n""" + basic_grammar += """ArgList -> ArgDesc | ArgDesc ArgList | ArgDesc ListSeparator ArgList\n""" + basic_grammar += """ListSeparator -> ',' | ', and' | 'and'\n""" + basic_grammar += """ArgDesc -> Value Paramname | Paramname Value | Paramname Filler Value | Value Filler Paramname\n""" + basic_grammar += """Filler -> 'is' | '=' | 'equal' | 'equals' | 'of' | 'for'\n""" + basic_grammar += """Value -> Number | Glayer | Dictionary | Boolean | List | Tuple | String | paramORvar\n""" + basic_grammar += """Vpair -> Value ',' Value | Value Value \n""" + basic_grammar += """Values -> Vpair | Vpair Values | Vpair ',' Values\n""" + basic_grammar += """Number -> 'nUMptindICatorWOrd'\n""" + basic_grammar += """String -> 'sTRingindICatorWOrd'\n""" + basic_grammar += """Parity -> '+' | '-' | 'positive' | 'negative' | 'plus' | 'minus'\n""" + basic_grammar += """Boolean -> 'true' | 'false' | 'True' | 'False'\n""" + basic_grammar += """List -> '[' Values ']' | '[' Value ']'\n""" + basic_grammar += """Tuple -> '(' Values ')' | '(' Value ')' | Values\n""" + basic_grammar += """Dictionary -> '{' keyvalpairs '}'\n""" + basic_grammar += f"""paramORvar -> {self.__cfgformat_from_list(known_paramsORvars)}\n""" + basic_grammar += """keyvalpairs -> Value ':' Value | Value ':' Value ',' keyvalpairs\n""" + basic_grammar += f"Glayer -> {self.__cfgformat_from_list(self.__construct_glayers().keys())}\n" + basic_grammar += f"Paramname -> {self.__cfgformat_from_list(self.names())}\n" + return basic_grammar + + def parse_user_params(self, known_paramsORvars: list, user_input_params: Optional[str]=None): + """Take user params as a string and parse+error check to produce a dictionary of params + Args: + user_input_params (str) + """ + if user_input_params is None or user_input_params.isspace() or len(user_input_params)==0: + return {} + # tokenize + tokens = self.__custom_tokenize(user_input_params) + # try to parse syntax + # formulate the grammar using the parameters + self.__grammar = nltk.CFG.fromstring(self.__get_str_grammar(known_paramsORvars)) + self.__grammar_parser = nltk.ChartParser(self.__grammar) + try: + mytree = list(self.__grammar_parser.parse(tokens))[0] + except Exception as serr: + raise ValueError("You may have provided a parameter that does not exist for this function OR there may be some other error") from serr + # reinsert numbers and strings + numlist_index = 0 + strlist_index = 0 + for pos in mytree.treepositions('leaves'): + if "nUMptindICatorWOrd" in mytree[pos]: + mytree[pos] = self.__number_list[numlist_index] + numlist_index += 1 + elif "sTRingindICatorWOrd" in mytree[pos]: + mytree[pos] = "\""+self.__string_list[strlist_index]+"\"" + strlist_index += 1 + # grab user params from the tree + user_params = dict() + for i, ArgDesc in enumerate(mytree.subtrees(lambda t: t.label()=="ArgDesc")): + # TODO: implement param name aliasing + paramname = "".join(list(ArgDesc.subtrees(lambda A: A.label()=="Paramname"))[0].leaves()) + # TODO: implement more sophisticated value parsing, and properly add double qoutes are glayers + value = " ".join(list(ArgDesc.subtrees(lambda A: A.label()=="Value"))[0].leaves()) + user_params[paramname] = self.noprintobj(value) + # split off kwargs into actual key word arguments in the params dictionary + if user_params.get("kwargs") is not None: + # TODO: maybe does not support edgecase where kwargs contains a dictionary? check + user_params[self.noprintobj("None")] = self.noprintobj("None,"+user_params["kwargs"].data.split("{")[-1].replace("}","")) + del user_params["kwargs"] + user_params = str(user_params).replace("None: None,","") + return user_params + + + + +###################################################### +# Data structures for possible operations with Glayout +###################################################### + +class GlayoutAction(ABC): + """each GlayoutAction corresponds to one or several Glayout code operations + GlayoutActions are convertible to code via the "get_code" method + + Args: + ABC (_type_): _description_ + """ + # GlayoutActions must be convertible to Glayout code via the get_code method + @abstractmethod + def get_code(self) -> str: + pass + + # GlayoutActions must have a test_suite to confirm functionality + @classmethod + @abstractmethod + def test(cls): + pass + + +class ImportCell(GlayoutAction): + """Import an existing CellFactory + The following information is stored: + identifiers: list[str] + handle: callable + relative_path: str + parameters: ParametersList + """ + #@validate_call + def __init__(self, component_identifiers: list[str], component_name: str, module_path: Optional[Union[str, Path]]=None, overwrite_module: bool=True): + """Store neccessary information + Will try to import the module; if it fails, will raise an exception + Args: + component_identifiers (list[str]): a list of potential names the user may use to identify this component + component_name (str): name of the thing we want to import from the target python module + module_path (str | Path | None): path of the module we want to import from + relative_path (bool): if true, takes the module_path directly as a python relative import path + """ + # try to find an os path to the desired module by looking in Glayout + if module_path is None: + # try to resolve path of glayout package + matching_files = list(Path("./").resolve().rglob("glayout")) + if len(matching_files)>0: + glayout_path = (matching_files[0].resolve() / "flow").resolve() + else: + raise FileNotFoundError("Glayout.flow package not found in a sub folder of ../ directory") + # try to resolve path of user module in Glayout package + # this will look for the file which starts with "component_name" ANYWHERE in the glayout.flow package + matching_files = list(glayout_path.rglob(str(component_name)+".py")) + matching_files += list(glayout_path.rglob(str(component_name)+".convo")) + # also check the test_cases directory for convo files + matching_files += list((glayout_path / "../llm/syntax_data/convos").rglob(str(component_name)+".convo")) + if len(matching_files)>0: + module_path = matching_files[-1].resolve() + else: + raise FileNotFoundError("Could not find a module called "+str(component_name)+" in Glayout") + # copy convo file into glayout Components and then create a python file and copy it into glayout components + if str(module_path).endswith(".convo"): + components_directory = glayout_path / "components" + if module_path.parent != components_directory: + shutil.copy(module_path, components_directory) + glayoutcode = glayout.syntaxer.dynamic_load.run_session(module_path,True) + component_name = glayout.syntaxer.dynamic_load.get_funccell_name(glayoutcode) + module_path = components_directory / (component_name + ".py") + # write python code from a convo to the components directory + with open(module_path,mode="w") as pycell: + pycell.write(glayoutcode) + # resolve module_path into a python relative import path (if it is not already so) + if any(ind in Path(module_path).as_posix() for ind in ["/",".py"]): + module_path = Path(module_path).resolve() + # error check that the path is indeed a python module and a real file + if module_path.suffix != ".py" or (not module_path.is_file()): + raise ValueError("module to import must be a python file ending in .py") + # if the module is not in the glayout.flow directory, it should be copied into current directory so it can be imported + module_path_parts = [part for part in module_path.parts] + if "flow" in module_path_parts: + module_path = "glayout." + ".".join(module_path_parts[module_path_parts.index("flow"):]).removesuffix(".py") + else: + # ensure the file does not already exist in the current directory, then copy + if Path("./"+module_path.name).is_file() and not overwrite_module: + raise ValueError("module name cannot conflict with existing module name in "+str(Path("./").resolve())) + shutil.copy(module_path,"./") + module_path = module_path.stem + # import callable (and make sure what we are importing is indeed callable) + python_import_path = str(module_path) + imported_obj = self.from_module_import(python_import_path, component_name) + if not callable(imported_obj): + raise TypeError("the thing you are trying to import must be callable") + # store relavent import information + self.identifiers = component_identifiers + self.handle = imported_obj + self.relative_path = python_import_path + self.parameters = ParametersList(imported_obj) + + @classmethod + def from_module_import(cls, module: str, nameofobjtoimport: str): + """this function is similar to the following line of python + from {module} import {nameofobjtoimport} + Returns: the object that was imported + """ + module_object = import_module(module) + return getattr(module_object, nameofobjtoimport) + + def get_code(self) -> str: + """returns as str a whole import line of code""" + return "from "+self.relative_path+" import "+self.handle.__name__ + + @classmethod + def test(cls): + tests = list() + tests.append([["nmos","nfet"],"nmos","glayout.flow.primitives.fet"]) + tests.append([["pmos","pfet"],"pmos","glayout.flow.primitives.fet"]) + tests.append([["guardring","tapring","welltap","well tap", "tap ring"],"tapring","glayout.flow.primitives.guardring"]) + tests.append([["mimcap"],"mimcap","glayout.flow.primitives.mimcap"]) + tests.append([["mimcap array","mimcaparray","mimcap_array"],"mimcap_array","glayout.flow.primitives.mimcap"]) + tests.append([["via","via stack","via_stack"],"via_stack","glayout.flow.primitives.via_gen"]) + tests.append([["via array","via_array"],"via_array","glayout.flow.primitives.via_gen"]) + tests.append([["nlp"],"test_stream","../interpreter/deprecated/../deprecated/practice_stream.py"]) + tests.append([["nmos","nfet"],"nmos","./glayout/primitives/fet.py"]) + #print("expected\t\t\tresult") + for testinst in tests: + print(ImportCell(*testinst).get_code()) + + +class CreateCellParameter(GlayoutAction): + """Create a new cell parameter + The following information is stored: + varname: str + type: Any + defaultvalue: int/float/None + reqs: tuple[opcode, value] + """ + #@validate_call + def __init__( + self, + varname: str, + vartype: Optional[Literal[int, float]]=None, + defaultvalue: Optional[Union[int,float]]=None, + requirements: Optional[tuple[Literal[">",">=","<","<=","!=","=="],Union[float,int]]]=None + ): + """Create a cell parameter + Args: + varname (str): name of parameter + vartype (Literal[int, float]): type of parameter is either int or float + defaultvalue: default value for parameter, Optional, None indicates no default + requirements: tuple[condition,value] where variable must be {condition} {value} + value can be a value or it can refer to another parameter + for example, variable must be <= 5 + """ + self.varname = str(varname) + self.type = vartype + self.defaultvalue = defaultvalue if self.type is None or defaultvalue is None else self.type(defaultvalue) + self.reqs = None + if requirements is not None: + required_op = None + if requirements[0]==">": + required_op = operator.gt + elif requirements[0]=="<": + required_op = operator.lt + elif requirements[0]==">=": + required_op = operator.ge + elif requirements[0]=="<=": + required_op = operator.le + elif requirements[0]=="==": + required_op = operator.eq + elif requirements[0]=="!=": + required_op = operator.ne + self.reqs=(required_op,requirements[1]) + + def get_code(self) -> str: + """returns as str a single parameter with type annotation to go in function definition + includes a tab in front and comma at the end + """ + param = self.varname + if self.type is not None: + param += ": "+self.type.__name__ + if self.defaultvalue is not None: + param += "="+str(self.defaultvalue) + if self.reqs is not None: + raise NotImplementedError("parameter error checking has not yet been implemented") + return "\t" + param + ", " + + def __str__(self) -> str: + return self.type.__name__ + " " + self.varname + ((" = "+str(self.defaultvalue)) if self.defaultvalue is not None else "") + + @classmethod + def test(cls): + tests = list() + tests.append(["width",float]) + tests.append(["length",float,0]) + tests.append(["fingers",int,3]) + for testinst in tests: + print(CreateCellParameter(*testinst).get_code()) + + +class CreateWorkingVariable(GlayoutAction): + """create a new working variable + The following information is stored: + varname: str + expression: str + """ + #@validate_call + def __init__(self, varname: str, input_expression: str): + """create a working variable + Args: + varname (str): name of working variable + input_expression (str): mathematical expression or value + """ + self.varname = str(varname) + self.expression = input_expression #sympify(input_expression) + #self.handle = symbols(self.varname) + + def get_code(self): + """return a complete line of code that defines the working variable""" + return self.varname + " = "+str(self.expression) + + @classmethod + def test(cls): + tests = list() + tests.append(["max_metal_sep","pdk.util_max_metal_sep()"]) + tests.append(["pi","3.1415"]) + for testinst in tests: + print(CreateWorkingVariable(*testinst).get_code()) + + +class PlaceCell(GlayoutAction): + """Place an existing cell at the origin + The following information is stored: + name: str (name for the component reference + _reference) + handle: Callable + params: parameters to pass to callable + """ + ref_suffix = "ref" + #@validate_call + def __init__(self, toplvl_name: str, generator_handle: Callable, component_name: str, user_input_parameters: str, known_paramsORvars: list): + """Store all information neccessary to place a cell without moving it anywhere + Args: + generator_handle (Callable): cell factory + component_name (str): name to assign to this component + user_input_parameters (str): just the portion of user input containing the parameters for the component + user input should be parameter name followed by value (optionally with a filler word e.g. [of, =, is]) + user input should contain NOTHING except parameters and values + known_paramsORvars (list): a list containing names of the currently defined parameters or variables + """ + # append to the place table + self.name = component_name + self.handle = generator_handle + self.params = ParametersList(generator_handle).parse_user_params(known_paramsORvars, user_input_parameters) + self.toplvl_name = toplvl_name + + def get_code(self) -> str: + """return 3 complete lines of code for placing a component""" + comment = f"# placing {self.name} centered at the origin" + l1 = f"{self.name} = {self.handle.__name__}(pdk,**{str(self.params)})" + l2 = f"{self.name}_{self.ref_suffix} = prec_ref_center({self.name})" + l3 = f"{self.toplvl_name}.add({self.name}_{self.ref_suffix})" + l4 = f"{self.toplvl_name}.add_ports({self.name}_{self.ref_suffix}.get_ports_list(),prefix=\"{self.name}_\")" + return comment + "\n" + l1 + "\n" + l2 + "\n" + l3 + "\n" + l4 + + @classmethod + def test(cls): + exp_name = "example_toplvl" + tests = list() + from glayout.flow.primitives.fet import nmos + tests.append([exp_name,nmos,"mirror","width of 4 length of 1"]) + for testinst in tests: + print(PlaceCell.get_code(*testinst)) + + +class AbsoluteMove(GlayoutAction): + """Move an existing Component by an absolute (x,y) distance + The following information is stored: + name: str (name of component to move should match the place table) + move_distance: tuple[float,float] (x,y) absolute move distance + """ + #@validate_call + def __init__(self, name_of_component_to_move: str, toplvl_name:str, absolute_move_info: tuple[float,float]): + self.name = str(name_of_component_to_move) + self.move_distance = eval(absolute_move_info) if isinstance(absolute_move_info,str) else absolute_move_info + self.toplvl_name = toplvl_name + + def get_code(self) -> str: + xmov = self.move_distance[0] + ymov = self.move_distance[1] + movecode = f"{self.name}_ref.movex({xmov}).movey({ymov})\n" + movecode += f"remove_ports_with_prefix({self.toplvl_name},\"{self.name}_\")\n" + movecode += f"{self.toplvl_name}.add_ports({self.name}_ref.get_ports_list(),prefix=\"{self.name}_\")" + return movecode + + @classmethod + def test(cls): + tests = list() + tests.append(["mirror","top",(4.2,3.5)]) + tests.append(["ref","top",(9,5)]) + for testinst in tests: + print(AbsoluteMove(*testinst).get_code()) + +class RelativeMove(GlayoutAction): + """Move an existing Component relative to another component + The following information is stored: + name: str (name of component to move should match the place table) + comp_id: str (name of the referenced component) + direction: tuple[int,int] either 0,1,-1 representing x,y multiplier to separation (up/north/above, right/east, left/west, down/south/below) + separation: str (variable or value but do not try to parse just send directly to code) + strdirection: str to use in creating the comment for the move + """ + move_index = int(0) + + #@validate_call + def __init__(self, name_of_component_to_move: str, toplvl_name: str, relative_comp: str, direction: str, separation: str="maxmetalsep"): + """Store all information neccessary to move a Component relative to another component + Args: + name_of_component_to_move (str): name of the component that will be moved + relative_comp (str): name of component that is taken as a reference for the move + direction (str): up/north/above, right/east, left/west, down/south/below + separation (str): value, variable, or expression to use for separation between relative comp and comp we are moving + """ + self.name = str(name_of_component_to_move) + self.relative_comp = str(relative_comp) + self.toplvl_name = toplvl_name + # TODO: more sophisticated validation that can distinguish float from var for separation + self.separation = str(separation) if separation is not None else "maxmetalsep" + # parse direction + direction = direction.lower().strip() + self.strdirection = direction + dirint = parse_direction(direction) + if dirint==1: + self.direction = (0,1) + elif dirint==2: + self.direction = (1,0) + elif dirint==0: + self.direction = (-1,0) + elif dirint==3: + self.direction = (0,-1) + else: + raise ValueError("invalid direction, move must be either up/north/above, right/east, left/west, or down/south/below") + + def get_code(self) -> str: + movinfo = [self.separation*direc for direc in self.direction] + l1 = f"# move {self.name} {self.strdirection} {self.relative_comp}" + l2 = None + if self.direction[0]==1:# move to the right + l2 = f"{self.direction[0]}*({self.separation} + center_to_edge_distance({self.relative_comp}_ref,3) + center_to_edge_distance({self.name}_ref,1))" + movfunc, cidx = "movex", 0 + if self.direction[0]==-1:# move to the left + l2 = f"{self.direction[0]}*({self.separation} + center_to_edge_distance({self.relative_comp}_ref,1) + center_to_edge_distance({self.name}_ref,3))" + movfunc, cidx = "movex", 0 + if self.direction[1]==1:# move to the north + l2 = f"{self.direction[1]}*({self.separation} + center_to_edge_distance({self.relative_comp}_ref,2) + center_to_edge_distance({self.name}_ref,4))" + movfunc, cidx = "movey", 1 + if self.direction[1]==-1:# move to the south + l2 = f"{self.direction[1]}*({self.separation} + center_to_edge_distance({self.relative_comp}_ref,4) + center_to_edge_distance({self.name}_ref,2))" + movfunc, cidx = "movey", 1 + if l2 is None: + raise ValueError("move must be either up/north/above, right/east, left/west, or down/south/below") + l2 = f"relativemovcorrection_{str(self.move_index)} = " + l2 + l3 = movfunc + f"({self.name}_ref,destination=(relativemovcorrection_{str(self.move_index)} + {self.relative_comp}_ref.center[{cidx}]))" + # update ports + l4 = f"remove_ports_with_prefix({self.toplvl_name},\"{self.name}_\")" + l5 = f"{self.toplvl_name}.add_ports({self.name}_ref.get_ports_list(),prefix=\"{self.name}_\")" + return l1 + "\n" + l2 + "\n" + l3 + "\n" + l4+ "\n" + l5 + + @classmethod + def test(cls): + raise NotImplementedError("testing RelativeMove has not yet been implemented") + + +class Route(GlayoutAction): + """Route between two existing Ports (port1->port2) + The following information is stored: + port1: str (name of first port) + port2: str (name of second port) + route_type: handle (c,l,or straight route) + params: dict paramters to pass to function + """ + #@validate_call + def __init__(self, toplvl_name: str, route_type: Callable, port1: str, port2: str, parameters: str, known_paramsORvars: list, compref: str=None): + """Store all neccessary information to route between two ports + Args: + route_type (Callable): l,c, straight, or smart route + port1 (str): + port2 (str): + parameters (str): user input parameters + known_paramsORvars (list): names of currently defined parameters and variables + compref (str, optional): if smart route this will be passed along + """ + self.port1 = str(port1) + self.port2 = str(port2) + self.route_type = route_type + # parse user params + self.params = ParametersList(self.route_type).parse_user_params(known_paramsORvars, parameters) + self.toplvl_name = toplvl_name + self.compref = compref + + def get_code(self) -> str: + port1s = f"{self.toplvl_name}.ports[\"{self.port1}\"]" + port2s = f"{self.toplvl_name}.ports[\"{self.port2}\"]" + if "smart" in self.route_type.__name__: + return f"{self.toplvl_name} << {self.route_type.__name__}(pdk,{port1s},{port2s},{self.compref},{self.toplvl_name},**{str(self.params)})" + return f"{self.toplvl_name} << {self.route_type.__name__}(pdk,{port1s},{port2s},**{str(self.params)})" + + @classmethod + def test(cls): + from glayout.flow.routing.c_route import c_route + tests = list() + tests.append([c_route,"name_of_port_1","name_of_port_2","cwidth of 4 width1=3"]) + for testinst in tests: + print(Route(*testinst).get_code()) + + + +# Top level Code Relational table +class GlayoutCode(GlayoutAction): + """Stores all needed information to create Glayout code in relation tables + GlayoutActions require some general context provided by the GlayoutCode class""" + + HEAD_MARKER = "####\n# Compiled Glayout\n# Apache License\n# Version 2.0, January 2004\n# http://www.apache.org/licenses/" + + # each table is implemented as a list of dictionaries + def __init__(self, toplvl_name: str): + self.toplvl_name = toplvl_name.replace(" ","_").strip() + # tables + self.import_table = list()# list of ImportCell type + self.parameter_table = list()# list of CreateCellParameter type + self.bulk_action_table = list()# list of Route, AbsoluteMove, RelativeMove, PlaceCell, CreateWorkingVariable + # initialize the import table to contain all primitives and routing primitives + self.__known_generator_ids = list()# list of str + self.__random_name_index = int(0) + # primitives + self.update_import_table(["nmos","nfet"],"nmos","glayout.flow.primitives.fet") + self.update_import_table(["pmos","pfet"],"pmos","glayout.flow.primitives.fet") + self.update_import_table(["guardring","tapring","welltap","well tap", "tap ring"],"tapring","glayout.flow.primitives.guardring") + self.update_import_table(["mimcap"],"mimcap","glayout.flow.primitives.mimcap") + self.update_import_table(["mimcap array","mimcaparray","mimcap_array"],"mimcap_array","glayout.flow.primitives.mimcap") + self.update_import_table(["via","via stack","via_stack"],"via_stack","glayout.flow.primitives.via_gen") + self.update_import_table(["via array","via_array"],"via_array","glayout.flow.primitives.via_gen") + # general components and layout strategies + two_nfet_interdigitized_aliases = ["interdigitized","interdigitated"]+list_cartesian_product(["interdigitized","interdigitated"],["nmos","nfet"],True) + self.update_import_table(two_nfet_interdigitized_aliases,"two_nfet_interdigitized","glayout.flow.placement.two_transistor_interdigitized") + generic_4T_interdigitzed_aliases = list_cartesian_product(list_cartesian_product(["four","4"], ["interdigitized","interdigitated"]),["fet","transistor"]) + self.update_import_table(generic_4T_interdigitzed_aliases, "generic_4T_interdigitzed", "glayout.flow.placement.four_transistor_interdigitized") + two_pfet_interdigitized_aliases = list_cartesian_product(["interdigitized","interdigitated"],["pmos","pfet"],True) + self.update_import_table(two_pfet_interdigitized_aliases,"two_pfet_interdigitized","glayout.flow.placement.two_transistor_interdigitized") + self.update_import_table(["diff pair","diff_pair","differential pair","differential pairs","differential transistor"],"diff_pair_generic","glayout.flow.components.diff_pair") + # import routing funcs + self.update_import_table(["smart route","smart","smart_route"],"smart_route","glayout.flow.routing.smart_route") + self.update_import_table(["L route","L_route","l route","l_route"],"L_route","glayout.flow.routing.L_route") + self.update_import_table(["C route","C_route","c route","c_route"],"c_route","glayout.flow.routing.c_route") + self.update_import_table(["straight route","straight_route"],"straight_route","glayout.flow.routing.straight_route") + # general utils are hardcoded imports in the get_code method + # add working variable max metal separation + self.update_variable_table("maxmetalsep","pdk.util_max_metal_seperation()") + self.update_variable_table("double_maxmetalsep","2*pdk.util_max_metal_seperation()") + self.update_variable_table("triple_maxmetalsep","3*pdk.util_max_metal_seperation()") + self.update_variable_table("quadruple_maxmetalsep","4*pdk.util_max_metal_seperation()") + self.__placed_noname_objs = dict() + + def search_import_table(self, generator_id: str) -> Callable: + """find a component with one identifier matching generator_id and return the function handle""" + handle = None + for generator in self.import_table: + if any(generator_id==identifier for identifier in generator.identifiers): + handle = generator.handle + if handle is None: + raise LookupError("the generator you were looking for was not found. Perhaps it uses a different alias?") + return handle + + def update_import_table(self, component_identifiers: list[str], component_name: Optional[str]=None, module_path: Optional[Union[str, Path]]=None): + """Add an ImportCell to the import_table, see ImportCell class""" + if component_name is None or component_name=="" or component_name.isspace(): + component_name = f"comp_{self.__random_name_index}" + self.__random_name_index += 1 + self.__known_generator_ids += component_identifiers + self.import_table.append(ImportCell(component_identifiers, component_name, module_path)) + + def update_parameter_table( + self, + varname: str, + vartype: Optional[Literal[int, float]]=None, + defaultvalue: Optional[Union[int,float]]=None, + requirements: Optional[tuple[Literal[">",">=","<","<=","!=","=="],Union[float,int]]]=None + ): + """Add a CreateCellParameter to the parameter_table, see CreateCellParameter class""" + self.parameter_table.append(CreateCellParameter(varname,vartype, defaultvalue, requirements)) + + def update_variable_table(self, varname: str, input_expression: str): + """Add a CreateWorkingVariable to the variable_table, see CreateWorkingVariable class""" + self.bulk_action_table.append(CreateWorkingVariable(varname, input_expression)) + + def update_place_table(self, generator_id: str, user_input_parameters: str, component_name: Optional[str]=None): + """Add a PlaceCell to the place_table, see PlaceCell class + Args: + generator_id (str): a known name for cell factory to use + component_name (str): name to assign to this component + user_input_parameters (str): just the portion of user input containing the parameters for the component + user input should be parameter name followed by value (optionally with a filler word e.g. [of, =, is]) + user input should contain NOTHING except parameters and values + """ + if component_name is None: + current_val = self.__placed_noname_objs.get(generator_id,int(0)) + self.__placed_noname_objs[generator_id] = current_val + 1 + component_name = generator_id + str(current_val) + self.bulk_action_table.append(PlaceCell(self.toplvl_name, self.search_import_table(generator_id), component_name, user_input_parameters, self.get_params_and_vars())) + + def update_move_table(self, move_type: str, name_of_component_to_move: str, *args, **kwargs): + """move_type can be absolute, relative""" + move_type = move_type.lower().strip() + if move_type=="absolute": + self.bulk_action_table.append(AbsoluteMove(name_of_component_to_move,self.toplvl_name,*args,**kwargs)) + elif move_type=="relative": + self.bulk_action_table.append(RelativeMove(name_of_component_to_move,self.toplvl_name,*args,**kwargs)) + + def update_route_table(self, port1: str, port2: str, parameters: str, route_type: Optional[str]=None): + # guess route type if not specified + if route_type is None: + route_type = "smart_route" + else: + # parse route type + route_type = str(route_type).lower().strip() + rpre = "L" if "l" in route_type else ("c" if "c" in route_type else ("straight" if "s" in route_type else None)) + rpre = "smart" if "sma" in route_type else rpre + if rpre is None: + rpre = "smart" + route_type = rpre + "_route" + # look for top comp ref this goes back to + if route_type=="smart_route": + for cellname in self.names_of_placed_cells(): + compref = cellname in port1 and cellname in port2 + if compref is not None: + compref = cellname + "_ref" + break + else: + compref = None + self.bulk_action_table.append(Route(self.toplvl_name,self.search_import_table(route_type),port1,port2,parameters,self.get_params_and_vars(),compref)) + + def get_params_and_vars(self) -> list[str]: + """Get the names of parameters and variables. + This method retrieves the names of parameters from the parameter table and the names of variables created + by 'CreateWorkingVariable' actions from the bulk action table. + Returns: + list[str]: A list containing the names of parameters and variables. + """ + params = [param.varname for param in self.parameter_table] + variables = [variable.varname for variable in self.bulk_action_table if isinstance(variable,CreateWorkingVariable)] + return params + variables + + def get_code(self) -> str: + # time this NLP was compiled to python code + current_time = datetime.datetime.now() + compilation_head = self.HEAD_MARKER + f"\n# {current_time}\n\n" + # produce all import code + import_code = "from glayout.flow.pdk.mappedpdk import MappedPDK\n" + import_code += "from gdsfactory import Component\n" + import_code += "from glayout.flow.pdk.util.comp_utils import move, movex, movey, prec_ref_center, evaluate_bbox, center_to_edge_distance\n" + import_code += "from glayout.flow.pdk.util.port_utils import remove_ports_with_prefix\n" + for comp_import in self.import_table: + import_code += comp_import.get_code() + "\n" + # create function header + function_head = f"def {self.toplvl_name}_cell(\n\tpdk: MappedPDK,\n" + for param in self.parameter_table: + function_head += param.get_code()+"\n" + function_head += "):\n" + # create variables, place, route, and move in the order that user supplied these directions + function_body = "pdk.activate()\n" + function_body += f"{self.toplvl_name} = Component(name=\"{self.toplvl_name}\")\n" + for bulk_action in self.bulk_action_table: + function_body += bulk_action.get_code()+"\n" + function_body = "\n".join(["\t"+line for line in function_body.splitlines()]) + # footer + footer = "\n\treturn "+self.toplvl_name+"\n" + return compilation_head + import_code + "\n" + function_head + function_body + footer + + @classmethod + def test(cls, test_all: bool=True): + glayout_actions = get_all_derived_classes(GlayoutAction) + glayout_actions.remove(cls) + print("now testing individual Glayout Actions") + for action in glayout_actions: + print(f"\ntesting{action.__name__}") + try: + action.test() + except Exception as e: + print(e) + print("\n\nnow testing GlayoutCode") + codeobj = GlayoutCode("newtoplvl") + codeobj.update_import_table(["stream","test_stream"],"test_stream","./deprecated/practice_stream.py") + codeobj.update_parameter_table("width",float,5.5) + codeobj.update_variable_table("int_width","int(width)") + codeobj.update_place_table("nfet","mirror","width of 5 length is 2") + codeobj.update_move_table("absolute","mirror",(3.3,3.3)) + codeobj.update_route_table("nfet_drain_west","nfet_source_west","","c route") + print(codeobj.get_code()) + + def names_of_placed_cells(self) -> list: + """Get the names of cells that have been placed. + Returns: + list: A list containing the names of cells that have been placed. + """ + names = list() + for action in self.bulk_action_table: + if isinstance(action,PlaceCell): + names.append(action.name) + return names + + def find_first_generator_id(self, sentence: str) -> str: + """returns the longest generator id in the given sentence + should only be used on sentences which for sure have a generator id in them + """ + # look for genids found in the sentence + genid_candidates = list() + for genid in self.__known_generator_ids: + if genid in sentence: + genid_candidates.append(genid) + # error checking + if len(genid_candidates) == 0: + print(self.__known_generator_ids) + print(sentence) + raise LookupError("no known generator id was found in the provided sentence") + # prefer longer strings + return max(genid_candidates, key=len) + + + + + + diff --git a/openfasoc/generators/glayout/requirements.txt b/openfasoc/generators/glayout/requirements.txt new file mode 100644 index 000000000..f13388908 --- /dev/null +++ b/openfasoc/generators/glayout/requirements.txt @@ -0,0 +1,19 @@ +gdsfactory==7.7.0 +prettyprint +prettyprinttree +nltk +torch +transformers +langchain +langchain_community +chromadb +ollama +unstructured +unstructured[md] +protobuf +sentence-transformers +peft +accelerate +bitsandbytes +safetensors +datasets diff --git a/openfasoc/generators/glayout/run.py b/openfasoc/generators/glayout/run.py new file mode 100644 index 000000000..aa5663bb4 --- /dev/null +++ b/openfasoc/generators/glayout/run.py @@ -0,0 +1,20 @@ +import argparse +from glayout.syntaxer.dynamic_load import run_session +from pathlib import Path + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Manage, interact, and run conversation sessions.") + parser.add_argument( + "-l", + "--load_conversation", + type=Path, + help="Specify the file path to load a previous conversation", + ) + parser.add_argument( + "-r", + "--restore_and_exit", + action='store_true', + help="Restore the conversation state and exit", + ) + args = parser.parse_args() + run_session(args.load_conversation, args.restore_and_exit) diff --git a/openfasoc/generators/glayout/setup.py b/openfasoc/generators/glayout/setup.py new file mode 100644 index 000000000..df1683b20 --- /dev/null +++ b/openfasoc/generators/glayout/setup.py @@ -0,0 +1,46 @@ +from setuptools import setup, find_packages + + +setup( + name='glayout', + version='0.0.4', + author='Ali Hammoud, Harsh Khandeparkar, Vijay Shankar, Chetanya Goyal, Sakib Pathen, Arlene Dai, Ryan Wans, Mehdi Saligane', + author_email='alibilal@umich.edu, Harsh, vijayshankar.renganathan@analog.com, Chetanya, spathen@umich.edu, arlendai@umich.edu, Ryan, mehdi@umich.edu', + description='A human language to analog layout API with support for different technologies.', + long_description=open('README.md').read(), + long_description_content_type='text/markdown', + url='https://github.com/idea-fasoc/OpenFASOC/tree/main/openfasoc/generators/glayout', + packages=find_packages(), + classifiers=[ + 'Programming Language :: Python :: 3.10', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + ], + python_requires='>=3.10', + install_requires=[ + "gdsfactory==7.7.0", + "prettyprint", + "prettyprinttree", + "nltk", + "torch", + "transformers", + "langchain", + "langchain_community", + "chromadb", + "ollama", + "unstructured", + "unstructured[md]" + "sentence-transformers", + "peft", + "accelerate", + "bitsandbytes", + "safetensors", + "requests", + "datasets" + ], + entry_points={ + 'console_scripts': [ + #"glayout = something:run"# Define any command line scripts here + ], + }, +) \ No newline at end of file diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/Makefile b/openfasoc/generators/glayout/tapeout/deprecated/Makefile similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/Makefile rename to openfasoc/generators/glayout/tapeout/deprecated/Makefile diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/common_source_amp.py b/openfasoc/generators/glayout/tapeout/deprecated/common_source_amp.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/common_source_amp.py rename to openfasoc/generators/glayout/tapeout/deprecated/common_source_amp.py diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/current_mirror.py b/openfasoc/generators/glayout/tapeout/deprecated/current_mirror.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/current_mirror.py rename to openfasoc/generators/glayout/tapeout/deprecated/current_mirror.py diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/magic_commands.tcl b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/magic_commands.tcl similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/magic_commands.tcl rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/magic_commands.tcl diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/run_drc.sh b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/run_drc.sh similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/run_drc.sh rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/run_drc.sh diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A.magicrc b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A.magicrc similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A.magicrc rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A.magicrc diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A_setup.tcl b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A_setup.tcl similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A_setup.tcl rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/drc-check/sky130A/sky130A_setup.tcl diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/line_res_gen.py b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/line_res_gen.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/line_res_gen.py rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/line_res_gen.py diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/merge_structures.py b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/merge_structures.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/merge_structures.py rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/merge_structures.py diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/pad_forty_met1_met5.GDS b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/pad_forty_met1_met5.GDS similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/pad_forty_met1_met5.GDS rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/pad_forty_met1_met5.GDS diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/via_chain_gen.py b/openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/via_chain_gen.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/line-res_via-chain/via_chain_gen.py rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/line-res_via-chain/via_chain_gen.py diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/magic_commands.tcl b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/magic_commands.tcl similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/magic_commands.tcl rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/magic_commands.tcl diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/run_drc.sh b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/run_drc.sh similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/run_drc.sh rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/run_drc.sh diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A.magicrc b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A.magicrc similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A.magicrc rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A.magicrc diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A_setup.tcl b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A_setup.tcl similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A_setup.tcl rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/drc-check/sky130A/sky130A_setup.tcl diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/mimcap_gen.py b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/mimcap_gen.py similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/mimcap_gen.py rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/mimcap_gen.py diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/pad_forty_met1_met5.GDS b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/pad_forty_met1_met5.GDS similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/pad_forty_met1_met5.GDS rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/pad_forty_met1_met5.GDS diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_1_FJK8MM.gds b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_1_FJK8MM.gds similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_1_FJK8MM.gds rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_1_FJK8MM.gds diff --git a/openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_2_FJK8MM.gds b/openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_2_FJK8MM.gds similarity index 100% rename from openfasoc/generators/gdsfactory-gen/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_2_FJK8MM.gds rename to openfasoc/generators/glayout/tapeout/deprecated/scripts/mimcap-array/sky130_fd_pr__cap_mim_m3_2_FJK8MM.gds diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/README.md b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/README.md similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/README.md rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/README.md diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/extract.bash.template b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/extract.bash.template similarity index 56% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/extract.bash.template rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/extract.bash.template index 309d60cb6..1a2bcb111 100644 --- a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/extract.bash.template +++ b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/extract.bash.template @@ -6,6 +6,28 @@ export PDK_ROOT=@@PDK_ROOT # args: # first arg = gds file to read # second arg = name of top cell in gds file to read +# third arg (optional) = noparasitics (basically an LVS extraction) + +paropt="@@@PAROPT" + +if [ "$paropt" = "noparasitics" ]; then + +magic -rcfile ./sky130A/sky130A.magicrc -noconsole -dnull << EOF +gds read $1 +flatten $2 +load $2 +select top cell +extract do local +extract all +ext2sim labels on +ext2sim +ext2spice lvs +ext2spice cthresh 0 +ext2spice -o $2_pex.spice +exit +EOF + +else magic -rcfile ./sky130A/sky130A.magicrc -noconsole -dnull << EOF gds read $1 @@ -25,7 +47,10 @@ ext2spice -o $2_pex.spice exit EOF +fi + rm -f $2.nodes rm -f $2.ext rm -f $2.res.ext rm -f $2.sim + diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/opamp_perf_eval.sp b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/opamp_perf_eval.sp similarity index 96% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/opamp_perf_eval.sp rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/opamp_perf_eval.sp index 34de866a4..652f37c97 100644 --- a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/opamp_perf_eval.sp +++ b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/opamp_perf_eval.sp @@ -6,7 +6,7 @@ ** of this file on line 6 as 25. DO NOT OVERRIDE. .temp {@@TEMP} -.save all +*.save all ** Define global parameters for altering .param bdp = 5u .param bcs = 5u @@ -17,10 +17,10 @@ Vsupply VDD GND 1.8 Vindc net1 GND 1 V2 vin net1 AC 0.5 V3 vip net1 AC -0.5 -.save i(vindc) -.save i(vsupply) -.save i(v2) -.save i(v3) +*.save i(vindc) +*.save i(vsupply) +*.save i(v2) +*.save i(v3) * bias currents Ibiasdp VDD biasdpn {bdp} @@ -48,9 +48,9 @@ Ibiaso VDD biason {bo} XDUT vo VDD vip vin biascsn biason biasdpn GND csoutputnetNC opamp * parameter sweep ** Run initial analysis -.save all -.options savecurrents -.ac dec 100 10 10G +*.save all +*.options savecurrents +*.ac dec 10 10 10G .control ** Set initial values set filetype = ascii @@ -98,7 +98,7 @@ let absolute_counter = 0 while bias_cs le bias_cs_Max while bias_dp le bias_dp_Max while bias_o le bias_o_Max - reset + *reset alter ibiascs = $&bias_cs alter ibiasdp = $&bias_dp alter ibiaso = $&bias_o @@ -107,7 +107,8 @@ while bias_cs le bias_cs_Max echo "Diff: $&bias_dp" echo "Out: $&bias_o" - run + save vo + ac dec 10 10 10G ** Find unity-gain bw point meas ac ugb_f when vdb(vo)=0 ** Measure phase margin @@ -174,6 +175,7 @@ let integ = integ(onoise_spectrum) let totalNoise = sqrt(integ[length(integ)-1]) wrdata result_noise.txt totalNoise +quit .endc .GLOBAL GND .GLOBAL VDD diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/pads/Manhattan120umPad.gds b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/pads/Manhattan120umPad.gds similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/pads/Manhattan120umPad.gds rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/pads/Manhattan120umPad.gds diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/pads/pad_60um_flat.gds b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/pads/pad_60um_flat.gds similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/pads/pad_60um_flat.gds rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/pads/pad_60um_flat.gds diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/pads/sky130_nano_pad.gds b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/pads/sky130_nano_pad.gds similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/pads/sky130_nano_pad.gds rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/pads/sky130_nano_pad.gds diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/requirements.txt b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/requirements.txt similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/requirements.txt rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/requirements.txt diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/cryo_models/nshort.spice b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/cryo_models/nshort.spice similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/cryo_models/nshort.spice rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/cryo_models/nshort.spice diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/cryo_models/nshortlvth.spice b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/cryo_models/nshortlvth.spice similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/cryo_models/nshortlvth.spice rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/cryo_models/nshortlvth.spice diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/cryo_models/pmos.spice b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/cryo_models/pmos.spice similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/cryo_models/pmos.spice rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/cryo_models/pmos.spice diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/sky130A.magicrc b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/sky130A.magicrc similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/sky130A.magicrc rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/sky130A.magicrc diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/sky130A_setup.tcl b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/sky130A_setup.tcl similarity index 100% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130A/sky130A_setup.tcl rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130A/sky130A_setup.tcl diff --git a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130_nist_tapeout.py b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py similarity index 97% rename from openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130_nist_tapeout.py rename to openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py index 6eac8b9f2..5bb77c37b 100644 --- a/openfasoc/generators/gdsfactory-gen/tapeout_and_RL/sky130_nist_tapeout.py +++ b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py @@ -1,19 +1,19 @@ import sys -from os import path +from os import path, rename # path to glayout -sys.path.append(path.join(path.dirname(__file__), '../')) +sys.path.append(path.join(path.dirname(__file__), '../../')) from gdsfactory.read.import_gds import import_gds from gdsfactory.components import text_freetype, rectangle -from glayout.pdk.util.comp_utils import prec_array, movey, align_comp_to_port, prec_ref_center -from glayout.pdk.util.port_utils import add_ports_perimeter, print_ports +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 gdsfactory.component import Component -from glayout.pdk.mappedpdk import MappedPDK -from glayout.components.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 glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.components.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 gdsfactory.cell import cell, clear_cache import numpy as np from subprocess import Popen @@ -32,10 +32,10 @@ from sklearn.cluster import KMeans, AgglomerativeClustering from sklearn.metrics import silhouette_score import argparse -from glayout.pdk.sky130_mapped import sky130_mapped_pdk as pdk +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as pdk from itertools import count, repeat -from glayout.pdk.util.snap_to_grid import component_snap_to_grid -from glayout.pdk.util.component_array_create import write_component_matrix +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 import re global _GET_PARAM_SET_LENGTH_ @@ -318,7 +318,7 @@ def opamp_results_de_serializer( results_dict["power_twostage"] = float(results[10]) return results_dict -def get_small_parameter_list(test_mode = False, clarge=True) -> np.array: +def get_small_parameter_list(test_mode = False, clarge=False) -> np.array: """creates small parameter list intended for brute force""" if not clarge: # all diffpairs to try @@ -537,7 +537,7 @@ def process_netlist_subckt(netlist: Union[str,Path], sim_model: Literal["normal else: headerstr = ".subckt opamp output vdd plus minus commonsourceibias outputibias diffpairibias gnd CSoutput" subckt_lines[i] = headerstr+"\nCload output gnd " + str(cload) +"p\n" - if ("floating" in line) or (noparasitics and len(line) and line[0]=="C"): + if ("floating" in line) or (noparasitics and len(line) and (line[0]=="c" or line[0]=="r")): subckt_lines[i] = "* "+ subckt_lines[i] if noparasitics: subckt_lines[i] = re.sub(r"ad=(\S*)","",subckt_lines[i]) @@ -577,7 +577,7 @@ def __run_single_brtfrc(index, parameters_ele, save_gds_dir, temperature_info: t params = opamp_parameters_de_serializer(parameters_ele) try: opamp_v = sky130_add_opamp_labels(sky130_add_lvt_layer(opamp(sky130pdk, **params))) - opamp_v.name = "opamp" + opamp_v.name = "opamp"+str(index) area = float(opamp_v.area()) # use temp dir with TemporaryDirectory() as tmpdirname: @@ -589,7 +589,7 @@ def __run_single_brtfrc(index, parameters_ele, save_gds_dir, temperature_info: t #import pdb; pdb.set_trace() with open("extract.bash.template","r") as extraction_script: extractbash_template = extraction_script.read() - extractbash_template = extractbash_template.replace("@@PDK_ROOT",PDK_ROOT) + extractbash_template = extractbash_template.replace("@@PDK_ROOT",PDK_ROOT).replace("@@@PAROPT","noparasitics" if noparasitics else "na") with open(str(tmpdirname)+"/extract.bash","w") as extraction_script: extraction_script.write(extractbash_template) #copyfile("extract.bash",str(tmpdirname)+"/extract.bash") @@ -599,8 +599,10 @@ def __run_single_brtfrc(index, parameters_ele, save_gds_dir, temperature_info: t Popen(["bash","extract.bash", tmp_gds_path, opamp_v.name],cwd=tmpdirname).wait() print("Running simulation at temperature: " + str(temperature_info[0]) + "C") process_spice_testbench(str(tmpdirname)+"/opamp_perf_eval.sp",temperature_info=temperature_info) - process_netlist_subckt(str(tmpdirname)+"/opamp_pex.spice", temperature_info[1], cload=cload, noparasitics=noparasitics) + process_netlist_subckt(str(tmpdirname)+"/opamp"+str(index)+"_pex.spice", temperature_info[1], cload=cload, noparasitics=noparasitics) + rename(str(tmpdirname)+"/opamp"+str(index)+"_pex.spice", str(tmpdirname)+"/opamp_pex.spice") # run sim and store result + #import pdb;pdb.set_trace() Popen(["ngspice","-b","opamp_perf_eval.sp"],cwd=tmpdirname).wait() ac_file = str(tmpdirname)+"/result_ac.txt" power_file = str(tmpdirname)+"/result_power.txt" @@ -677,7 +679,7 @@ def single_build_and_simulation(parameters: np.array, temp: int=25, output_dir: saves opamp gds in current directory with name 12345678987654321.gds returns -987.654321 for all values IF phase margin < 45 """ - from glayout.pdk.sky130_mapped import sky130_mapped_pdk + from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk # process temperature info temperature_info = [temp, None] if temperature_info[0] > -20: @@ -1280,7 +1282,7 @@ def create_opamp_matrix(save_dir_name: str, params: np.array, results: Optional[ elif args.mode == "gen_opamps": global usepdk if args.pdk[0].lower()=="g": - from glayout.pdk.gf180_mapped import gf180_mapped_pdk + from glayout.flow.pdk.gf180_mapped import gf180_mapped_pdk usepdk = gf180_mapped_pdk else: usepdk = pdk diff --git a/requirements.txt b/requirements.txt index ccb4ee1ce..f48a42e9d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,9 +2,13 @@ pandas klayout numpy matplotlib -gdsfactory +gdsfactory==7.7.0 nbsphinx cairosvg scipy ltspice mako +prettyprinttree +scikit-learn +scipy +seaborn diff --git a/setup.py b/setup.py index 2a2bd174b..a971fcf54 100644 --- a/setup.py +++ b/setup.py @@ -23,11 +23,11 @@ def get_install_requires(): long_description_content_type="text/markdown", packages=find_packages(exclude=("tests",)), install_requires=get_install_requires(), - python_requires=">=3.7", + python_requires=">=3.10", classifiers=[ "Development Status :: 2 - Pre-Alpha", "License :: OSI Approved :: MIT License", "Programming Language :: Python", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.10", ], )