diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_WL.py b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_WL.py new file mode 100644 index 000000000..cd02159e4 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_WL.py @@ -0,0 +1,335 @@ +import sys +sys.path.append('../../elementary/current_mirror/') +sys.path.append('../../composite/') + +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as sky130 +from glayout.flow.pdk.gf180_mapped import gf180_mapped_pdk as gf180 + +from glayout.flow.primitives.guardring import tapring +from glayout.flow.routing.smart_route import smart_route +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 gdsfactory.cell import cell, clear_cache +from gdsfactory.component import Component, copy +from gdsfactory.component_reference import ComponentReference +from gdsfactory.components.rectangle import rectangle +from glayout.flow.pdk.mappedpdk import MappedPDK +from typing import Optional, Union +from pydantic import validate_arguments + +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized + +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, prec_array, movey, align_comp_to_port, prec_ref_center +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.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 gdsfactory.components import text_freetype, rectangle +from gdsfactory import Component +from gdsfactory.routing.route_quad import route_quad +from glayout.flow.spice.netlist import Netlist +from typing import Optional, Union + + + +# @validate_arguments +def generate_current_mirror_netlist( + pdk: MappedPDK, + instance_name: str, + CM_size: tuple[float, float, int], # (width, length, multipliers) + drain_net_ref: str, + drain_net_copy: str, + source_net_ref: str, + source_net_copy: str, + gate_net: str, + transistor_type: str = "nfet", + bulk_net: str = None, + proposed_ground: str = None, # Proposed ground net + dummy: bool = True, + subckt_only: bool = False, + show_netlist: bool = False, + **kwargs + ) -> Netlist: + """Generate a netlist for a current mirror.""" + + if bulk_net is None: + bulk_net = "VDD" if transistor_type.lower() == "pfet" else "VSS" + + width = CM_size[0] + length = CM_size[1] + multipliers = CM_size[2] + fingers = CM_size[3] # Number of fingers of the interdigitized fets + mtop = multipliers * fingers if subckt_only else 1 + #mtop = multipliers * 2 if dummy else multipliers # Double the multiplier to account for the dummies + + model_name = pdk.models[transistor_type.lower()] + + circuit_name = instance_name + nodes = list(set([drain_net_ref, gate_net, drain_net_copy, source_net_ref,source_net_copy,bulk_net])) # Take only unique NET names + + source_netlist = f".subckt {circuit_name} {' '.join(nodes)}\n" + + #source_netlist += f"V{proposed_ground}1 ({proposed_ground} {bulk_net}) 0\n" #Proposed ground connection + + # Generating only two transistors (one on each side): + source_netlist += f"XA {drain_net_ref} {gate_net} {source_net_ref} {bulk_net} {model_name} l={length} w={width} m={mtop}\n" + source_netlist += f"XB {drain_net_copy} {gate_net} {source_net_copy} {bulk_net} {model_name} l={length} w={width} m={mtop}\n" + if dummy: + source_netlist += f"XDUMMY {bulk_net} {bulk_net} {bulk_net} {bulk_net} {model_name} l={length} w={width} m={mtop}\n" + source_netlist += ".ends " + circuit_name + + instance_format = "X{name} {nodes} {circuit_name} l={length} w={width} m={mult}" + + topnet=Netlist( + circuit_name=circuit_name, + nodes=nodes, + source_netlist=source_netlist, + instance_format=instance_format, + parameters={ + "model": model_name, + "width": width, + "length": length, + 'mult': multipliers,}, + ) + if show_netlist: + generated_netlist_for_lvs = topnet.generate_netlist() + print(f"Generated netlist :\n", generated_netlist_for_lvs) + + file_path_local_storage = "./gen_netlist.txt" + try: + with open(file_path_local_storage, 'w') as file: + file.write(generated_netlist_for_lvs) + except: + print(f"Verify the file availability and type: ", generated_netlist_for_lvs, type(generated_netlist_for_lvs)) + return topnet + + +# @validate_arguments +def current_mirror_base( + pdk: MappedPDK, + Width: float = 1, + Length: Optional[float] = None, + num_cols: int = 2, + fingers: int = 1, + type: Optional[str] = 'nfet', + with_substrate_tap: Optional[bool] = False, + with_tie: Optional[bool] = True, + with_dummy: Optional[bool] = True, + tie_layers: tuple[str,str]=("met2","met1"), + show_netlist: Optional[bool] = False, + **kwargs + ) -> Component: + + """An instantiable current mirror that returns a Component object. + The current mirror could be 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 + This current mirror is used to generate a exact copy of the reference current. + [TODO] Needs to be checked for both pfet and nfet configurations. + [TODO] It will be updated with multi-leg or stackked length parametrization in future. + [TODO] There will also be a Regulated Cascoded block added to it. + + Args: + pdk (MappedPDK): the process design kit to use + Width (float): width of the interdigitized fets (same for both reference and mirror) + Length (float): length of the interdigitized fets (same for both reference and mirror) + As Default, Set to None to use the minimum length of the technology + numcols (int): number of columns of the interdigitized fets + fingers: Number of fingers of interdigitized fets (same for both reference and mirror) + 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 + Returns: + Component: a current mirror component object + """ + pdk.activate() + maxmet_sep = pdk.util_max_metal_seperation() + psize=(0.35,0.35) + # Create the current mirror component + CurrentMirror = Component(name="CurrentMirror") + Length = Length if Length is not None else pdk.get_grule('poly')['min_width'] + + # Create the interdigitized fets + if type.lower() =="pfet" or type.lower() =="pmos": + currm= two_pfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers, + dummy=with_dummy,with_substrate_tap=with_substrate_tap,with_tie=with_tie,tie_layers=tie_layers) + well, sdglayer = "nwell", "p+s/d" + elif type.lower() =="nfet" or type.lower() =="nmos": + currm= two_nfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers,dummy=with_dummy, + with_substrate_tap=with_substrate_tap,with_tie=with_tie,tie_layers=tie_layers) + well, sdglayer = "pwell", "n+s/d" + else: + raise ValueError("type must be either nfet or pfet") + + # Add the interdigitized fets to the current mirror top component + currm_ref = prec_ref_center(currm) + CurrentMirror.add(currm_ref) + CurrentMirror.add_ports(currm_ref.get_ports_list(),prefix="currm_") + + # Connecting the source and gate of the fets + gate_short = CurrentMirror << c_route(pdk,CurrentMirror.ports["currm_A_gate_W"],CurrentMirror.ports["currm_B_gate_W"],extension=3*maxmet_sep) + CurrentMirror.add_ports(gate_short.get_ports_list(), prefix="gateshortports_") + + + CurrentMirror << L_route(pdk,CurrentMirror.ports["currm_A_drain_W"],gate_short.ports["con_S"],fullbottom=True) + + source_short = CurrentMirror << c_route(pdk,CurrentMirror.ports["currm_A_source_E"],CurrentMirror.ports["currm_B_source_E"],fullbottom=True) + + # Connecting dummies to the welltie + if with_dummy: + try: + CurrentMirror << straight_route(pdk, CurrentMirror.ports["A_0_dummy_L_gsdcon_top_met_W"],CurrentMirror.ports["welltie_W_top_met_W"],glayer2="met1") + except KeyError: + pass + try: + end_col = num_cols - 1 + port1 = f'B_{end_col}_dummy_R_gdscon_top_met_E' + CurrentMirror << straight_route(pdk, CurrentMirror.ports[port1], CurrentMirror.ports["welltie_E_top_met_E"], glayer2="met1") + except KeyError: + pass + + + # add well + CurrentMirror.add_padding(default=pdk.get_grule(well, "active_tap")["min_enclosure"],layers=[pdk.get_glayer(well)]) + CurrentMirror = add_ports_perimeter(CurrentMirror, layer = pdk.get_glayer(well), prefix="well_") + + #Connecting the source of the fets to the bulk ??? + src2bulk=CurrentMirror << straight_route(pdk, source_short.ports["con_N"],CurrentMirror.ports["currm_welltie_N_top_met_E"], glayer2="met2") + CurrentMirror.add_ports(src2bulk.get_ports_list(), prefix="purposegndport_") + + ##The default naming scheme of ports in GDSFactory + ##e1=West, e2=North, e3=East, e4=South. The default naming scheme of ports in GDSFactory + + # place vref pin (Needs more work to place it properly) + Irefpin = CurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Irefpin.movex(evaluate_bbox(Irefpin)[0]+(num_cols*maxmet_sep)).movey(CurrentMirror.ymax) + # route vref to drain of A + CurrentMirror << smart_route(pdk, CurrentMirror.ports["currm_A_drain_W"], Irefpin.ports["e4"]) + ## align_comp_to_port(vrefpin,ss.ports["top_met_E"], alignment=('c', 'b')) ?? How to align it properly + + + # place vcopy pin (Needs more work to place it properly) + Icopypin = CurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Icopypin.movex(evaluate_bbox(Icopypin)[0]-(num_cols*maxmet_sep)).movey(CurrentMirror.ymax) + # route vcopy to drain of B + CurrentMirror << smart_route(pdk, CurrentMirror.ports["currm_B_drain_W"], Icopypin.ports["e4"]) + ## align_comp_to_port(vrefpin,ss.ports["top_met_E"], alignment=('c', 'b')) ?? How to align it properly + + CurrentMirror.add_ports(Irefpin.get_ports_list(), prefix="refport_") + CurrentMirror.add_ports(Icopypin.get_ports_list(), prefix="copyport_") + + + CurrentMirror.info["netlist"] = generate_current_mirror_netlist( + pdk=pdk, + instance_name=CurrentMirror.name, + CM_size= (Width, Length, num_cols,fingers), # (width, length, multipliers, fingers) + transistor_type=type, + drain_net_ref="IREF", # Input drain connected to VREF + drain_net_copy="ICOPY", # Output drain connected to VCOPY + gate_net="IREF", # Gate connected to VREF + source_net_ref="VSS" if type=="nfet" else "VDD", # Source connected to VSS + source_net_copy="VSS" if type=="nfet" else "VDD", # Source connected to VSS + bulk_net= "VSS" if type=="nfet" else "VDD", #Proposed ground should also change + subckt_only=True, + show_netlist=show_netlist, + ) + + return rename_ports_by_orientation(component_snap_to_grid(CurrentMirror)) + + +def sky130_add_current_mirror_labels( + CMS: Component, + transistor_type: str = "nfet", + pdk: MappedPDK =sky130 + ) -> Component: + """Add labels to the current mirror layout for LVS, handling both nfet and pfet.""" + + #Would be adjusted for pdk agonastic later + met2_pin = (69, 16) + met2_label = (69, 5) + met3_pin = (70, 16) + met3_label = (70, 5) + + CMS.unlock() + move_info = [] + psize=(0.35,0.35) + + + # VREF label (for both gate and drain of transistor A, and dummy drains) + Iref_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Iref_label.add_label(text="IREF", layer=met2_label) + move_info.append((Iref_label, CMS.ports["refport_N"], None)) # Drain of A + #move_info.append((Iref_label, CMS.ports["gateshortports_con_N"], None)) # Gate of A & B + + # VCOPY label (for drain of transistor B) + Icopy_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Icopy_label.add_label(text="ICOPY", layer=met2_label) + move_info.append((Icopy_label, CMS.ports["copyport_N"], None)) # Drain of B + + # VSS/VDD label (for sources/bulk connection) + if transistor_type.lower() == "nfet": + bulk_net_name = "VSS" + bulk_pin_layer = met2_pin + bulk_label_layer = met2_label + else: # pfet + bulk_net_name = "VDD" + bulk_pin_layer = met2_pin + bulk_label_layer = met2_label + + ##Need to clarify the bulk and source connection?? + # VB label + vb_label = rectangle(layer=bulk_pin_layer, size=psize, centered=True).copy() + vb_label.add_label(text=bulk_net_name , layer=bulk_label_layer) + move_info.append((vb_label, CMS.ports["purposegndport_route_N"], None)) + + # Add labels to the component + for label, port, alignment in move_info: + if port: + alignment = ('c', 'b') if alignment is None else alignment + aligned_label = align_comp_to_port(label, port, alignment=alignment) + CMS.add(aligned_label) + + return CMS.flatten() + + +## To Test their primitives +# from current_mirror import current_mirror, current_mirror_netlist + +if __name__ == "__main__": + comp = current_mirror_base(sky130, numcols=2, device='nfet',show_netlist=True) + #comp.pprint_ports() + comp = sky130_add_current_mirror_labels(comp, transistor_type='nfet', pdk=sky130) + comp.show() + + + # # Write the current mirror layout to a GDS file + comp.name = "CM" + # delete_files_in_directory("GDS/") + # tmpdirname = Path("GDS/").resolve() + # delete_files_in_directory("GDS/") + # tmp_gds_path = Path(comp.write_gds(gdsdir=tmpdirname)).resolve() + comp.write_gds("./CM.gds") + comp.show() + # # Generate the netlist for the current mirror + # print("\n...Generating Netlist...") + # print(comp.info["netlist"].generate_netlist()) + # # DRC Checks + # #delete_files_in_directory("DRC/") + print("\n...Running DRC...") + drc_result = sky130.drc_magic(comp, "CM") + #drc_result = sky130.drc_magic(comp, "CM",output_file="DRC/") + print(drc_result['result_str']) + # # LVS Checks + # #delete_files_in_directory("LVS/") + print("\n...Running LVS...") + netgen_lvs_result = sky130.lvs_netgen(comp, "CM") + # #netgen_lvs_result = sky130.lvs_netgen(comp, "CM",output_file_path="LVS/") + # print(netgen_lvs_result['result_str']) + diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_primitive.png b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_primitive.png new file mode 100644 index 000000000..beba36d2e Binary files /dev/null and b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_primitive.png differ diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_primitive.py b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_primitive.py new file mode 100644 index 000000000..e8701c763 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/CM_primitive.py @@ -0,0 +1,360 @@ +import sys +sys.path.append('../../elementary/current_mirror/') +sys.path.append('../../composite/') + +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as sky130 +from glayout.flow.pdk.gf180_mapped import gf180_mapped_pdk as gf180 + +from glayout.flow.primitives.guardring import tapring +from glayout.flow.routing.smart_route import smart_route +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.routing.c_route import c_route +from glayout.flow.routing.L_route import L_route +from glayout.flow.primitives.via_gen import via_stack + +from gdsfactory.cell import cell, clear_cache +from gdsfactory.component import Component, copy +from gdsfactory.component_reference import ComponentReference +from gdsfactory.components.rectangle import rectangle +from glayout.flow.pdk.mappedpdk import MappedPDK +from typing import Optional, Union +from pydantic import validate_arguments + +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized + +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, prec_array, movey, align_comp_to_port, prec_ref_center +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.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 gdsfactory.components import text_freetype, rectangle +from gdsfactory import Component +from gdsfactory.routing.route_quad import route_quad +from glayout.flow.spice.netlist import Netlist +from typing import Optional, Union + + + +# @validate_arguments +def generate_current_mirror_netlist( + pdk: MappedPDK, + instance_name: str, + CM_size: tuple[float, float, int], # (width, length, multipliers) + drain_net_ref: str, + drain_net_copy: str, + source_net_ref: str, + source_net_copy: str, + gate_net: str, + transistor_type: str = "nfet", + bulk_net: str = None, + proposed_ground: str = None, # Proposed ground net + dummy: bool = True, + subckt_only: bool = False, + show_netlist: bool = False, + **kwargs + ) -> Netlist: + """Generate a netlist for a current mirror.""" + + if bulk_net is None: + bulk_net = "VDD" if transistor_type.lower() == "pfet" else "VSS" + + width = CM_size[0] + length = CM_size[1] + multipliers = CM_size[2] + fingers = CM_size[3] # Number of fingers of the interdigitized fets + mtop = multipliers * fingers if subckt_only else 1 + #mtop = multipliers * 2 if dummy else multipliers # Double the multiplier to account for the dummies + + model_name = pdk.models[transistor_type.lower()] + + circuit_name = instance_name + nodes = list(set([drain_net_ref, gate_net, drain_net_copy, source_net_ref,source_net_copy,bulk_net])) # Take only unique NET names + + source_netlist = f".subckt {circuit_name} {' '.join(nodes)}\n" + + #source_netlist += f"V{proposed_ground}1 ({proposed_ground} {bulk_net}) 0\n" #Proposed ground connection + + # Generating only two transistors (one on each side): + source_netlist += f"XA {drain_net_ref} {gate_net} {source_net_ref} {bulk_net} {model_name} l={length} w={width} m={mtop}\n" + source_netlist += f"XB {drain_net_copy} {gate_net} {source_net_copy} {bulk_net} {model_name} l={length} w={width} m={mtop}\n" + if dummy: + source_netlist += f"XDUMMY {bulk_net} {bulk_net} {bulk_net} {bulk_net} {model_name} l={length} w={width} m={mtop}\n" + source_netlist += ".ends " + circuit_name + + instance_format = "X{name} {nodes} {circuit_name} l={length} w={width} m={mult}" + + topnet=Netlist( + circuit_name=circuit_name, + nodes=nodes, + source_netlist=source_netlist, + instance_format=instance_format, + parameters={ + "model": model_name, + "width": width, + "length": length, + 'mult': multipliers,}, + ) + if show_netlist: + generated_netlist_for_lvs = topnet.generate_netlist() + print(f"Generated netlist :\n", generated_netlist_for_lvs) + + file_path_local_storage = "./gen_netlist.txt" + try: + with open(file_path_local_storage, 'w') as file: + file.write(generated_netlist_for_lvs) + except: + print(f"Verify the file availability and type: ", generated_netlist_for_lvs, type(generated_netlist_for_lvs)) + return topnet + + +# @validate_arguments +def current_mirror_base( + pdk: MappedPDK, + Width: float = 1, + Length: Optional[float] = None, + num_cols: int = 2, + fingers: int = 1, + type: Optional[str] = 'nfet', + with_substrate_tap: Optional[bool] = False, + with_tie: Optional[bool] = True, + with_dummy: Optional[bool] = True, + show_netlist: Optional[bool] = False, + **kwargs + ) -> Component: + + """An instantiable current mirror that returns a Component object. + The current mirror could be 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 + This current mirror is used to generate a exact copy of the reference current. + [TODO] Needs to be checked for both pfet and nfet configurations. + [TODO] It will be updated with multi-leg or stackked length parametrization in future. + [TODO] There will also be a Regulated Cascoded block added to it. + + Args: + pdk (MappedPDK): the process design kit to use + Width (float): width of the interdigitized fets (same for both reference and mirror) + Length (float): length of the interdigitized fets (same for both reference and mirror) + As Default, Set to None to use the minimum length of the technology + numcols (int): number of columns of the interdigitized fets + fingers: Number of fingers of interdigitized fets (same for both reference and mirror) + 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 + Returns: + Component: a current mirror component object + """ + pdk.activate() + maxmet_sep = pdk.util_max_metal_seperation() + psize=(0.35,0.35) + # Create the current mirror component + CurrentMirror = Component(name="CurrentMirror") + Length = Length if Length is not None else pdk.get_grule('poly')['min_width'] + + # Create the interdigitized fets + if type.lower() =="pfet" or type.lower() =="pmos": + currm= two_pfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers, + dummy=with_dummy,with_substrate_tap=with_substrate_tap,with_tie=with_tie) + well, sdglayer = "nwell", "p+s/d" + elif type.lower() =="nfet" or type.lower() =="nmos": + currm= two_nfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers,dummy=with_dummy, + with_substrate_tap=with_substrate_tap,with_tie=with_tie) + well, sdglayer = "pwell", "n+s/d" + else: + raise ValueError("type must be either nfet or pfet") + + # Add the interdigitized fets to the current mirror top component + currm_ref = prec_ref_center(currm) + CurrentMirror.add(currm_ref) + CurrentMirror.add_ports(currm_ref.get_ports_list(),prefix="currm_") + + # for absc in CurrentMirror.ports.keys(): + # if len(absc.split("_")) <= 5: + # if set(["currm","A","W"]).issubset(set(absc.split("_"))): + # print(absc+"\n") + + #Routing + viam2m3 = via_stack(pdk, "met2", "met3", centered=True) + drain_A_via = CurrentMirror << viam2m3 + drain_A_via.move(CurrentMirror.ports["currm_A_0_drain_W"].center).movex(-1) + source_A_via = CurrentMirror << viam2m3 + source_A_via.move(CurrentMirror.ports[f"currm_A_{num_cols-1:d}_source_E"].center).movex(0.5*(num_cols)) + gate_A_via = CurrentMirror << viam2m3 + gate_A_via.move(CurrentMirror.ports["currm_A_0_gate_W"].center).movex(-1-2*Length) + + drain_B_via = CurrentMirror << viam2m3 + drain_B_via.move(drain_A_via.center).movex(-1+Length).movey(+1+maxmet_sep-Length) + source_B_via = CurrentMirror << viam2m3 + source_B_via.move(source_A_via.center).movey(1+maxmet_sep-Length) + gate_B_via = CurrentMirror << viam2m3 + gate_B_via.move(gate_A_via.center).movey(-1-maxmet_sep+Length) + ##################### + CurrentMirror << straight_route(pdk,currm_ref.ports["A_0_drain_E"], drain_A_via.ports["bottom_met_W"]) + CurrentMirror << straight_route(pdk,currm_ref.ports["B_0_drain_E"], drain_B_via.ports["bottom_met_W"]) + ##################### + CurrentMirror << straight_route(pdk,currm_ref.ports["A_0_source_E"], source_A_via.ports["bottom_met_W"]) + CurrentMirror << straight_route(pdk,currm_ref.ports["B_0_source_E"], source_B_via.ports["bottom_met_W"]) + + source_short = CurrentMirror << straight_route(pdk, source_A_via.ports["top_met_N"], source_B_via.ports["top_met_S"]) + #,extension=1.2*max(Width,Width), width1=psize[0], width2=ps, cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met2") + ##################### + CurrentMirror << straight_route(pdk,currm_ref.ports["A_0_gate_W"], gate_A_via.ports["bottom_met_W"]) + CurrentMirror << straight_route(pdk,currm_ref.ports["B_0_gate_W"], gate_B_via.ports["bottom_met_W"]) + ##################### + gate_short = CurrentMirror << straight_route(pdk, gate_A_via.ports["top_met_S"], gate_B_via.ports["top_met_N"]) + + # #connecting the Drian of A to gate short + CurrentMirror << straight_route(pdk,drain_A_via.ports["top_met_S"],gate_short.ports["route_N"]) + + # # Connecting dummies to the welltie + # if with_dummy: + # try: + # CurrentMirror << straight_route(pdk, CurrentMirror.ports["A_0_dummy_L_gsdcon_top_met_W"],CurrentMirror.ports["welltie_W_top_met_W"],glayer2="met1") + # except KeyError: + # pass + # try: + # end_col = num_cols - 1 + # port1 = f'B_{end_col}_dummy_R_gdscon_top_met_E' + # CurrentMirror << straight_route(pdk, CurrentMirror.ports[port1], CurrentMirror.ports["welltie_E_top_met_E"], glayer2="met1") + # except KeyError: + # pass + + # # add well + # CurrentMirror.add_padding(default=pdk.get_grule(well, "active_tap")["min_enclosure"],layers=[pdk.get_glayer(well)]) + # CurrentMirror = add_ports_perimeter(CurrentMirror, layer = pdk.get_glayer(well), prefix="well_") + #if well == "nwell": + # CurrentMirror.add_padding(layers=(pdk.get_glayer("nwell"),),default= 1 ) + + + #Connecting the source of the fets to the bulk ??? + src2bulk=CurrentMirror << straight_route(pdk, source_short.ports["route_N"],CurrentMirror.ports["currm_welltie_N_top_met_E"], glayer2="met2") + + ##The default naming scheme of ports in GDSFactory + ##e1=West, e2=North, e3=East, e4=South. The default naming scheme of ports in GDSFactory + + ########################################################### + Irefpin = CurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Irefpin.move(drain_A_via.center).movey(0.2*evaluate_bbox(currm_ref)[0]) + CurrentMirror << straight_route(pdk, drain_A_via.ports["top_met_N"],Irefpin.ports["e4"], glayer2="met3") + + Icopypin = CurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Icopypin.move(drain_A_via.center).movex(-1+Length).movey(0.2*evaluate_bbox(currm_ref)[0]) + CurrentMirror << straight_route(pdk, drain_B_via.ports["top_met_N"],Icopypin.ports["e4"], glayer2="met3") + + bulkpin = CurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + bulkpin.move(source_A_via.center).movey(0.2*evaluate_bbox(currm_ref)[0]) + CurrentMirror << straight_route(pdk, src2bulk["route_N"],bulkpin.ports["e4"], glayer2="met3") + ########################################################### + + CurrentMirror.add_ports(drain_A_via.get_ports_list(), prefix="A_drain_") + CurrentMirror.add_ports(drain_B_via.get_ports_list(), prefix="B_drain_") + CurrentMirror.add_ports(gate_short.get_ports_list(), prefix="gateshortports_") + CurrentMirror.add_ports(source_short.get_ports_list(), prefix="sourceshortports_") + CurrentMirror.add_ports(src2bulk.get_ports_list(), prefix="purposegndport_") + CurrentMirror.add_ports(Irefpin.get_ports_list(), prefix="refport_") + CurrentMirror.add_ports(Icopypin.get_ports_list(), prefix="copyport_") + CurrentMirror.add_ports(bulkpin.get_ports_list(), prefix="bulkport_") + + CurrentMirror = component_snap_to_grid(rename_ports_by_orientation(CurrentMirror)) + + CurrentMirror.info["netlist"] = generate_current_mirror_netlist( + pdk=pdk, + instance_name=CurrentMirror.name, + CM_size= (Width, Length, num_cols,fingers), # (width, length, multipliers, fingers) + transistor_type=type, + drain_net_ref="IREF", # Input drain connected to IREF + drain_net_copy="ICOPY", # Output drain connected to ICOPY + gate_net="IREF", # Gate connected to VREF + source_net_ref="VSS" if type=="nfet" else "VDD", # Source connected to VSS + source_net_copy="VSS" if type=="nfet" else "VDD", # Source connected to VSS + bulk_net= "VSS" if type=="nfet" else "VDD", #Proposed ground should also change + subckt_only=True, + show_netlist=show_netlist, + ) + + return CurrentMirror + + +def sky130_add_current_mirror_labels( + CMS: Component, + transistor_type: str = "nfet", + pdk: MappedPDK =sky130 + ) -> Component: + """Add labels to the current mirror layout for LVS, handling both nfet and pfet.""" + + #Would be adjusted for pdk agonastic later + met2_pin = (69, 16) + met2_label = (69, 5) + met3_pin = (70, 16) + met3_label = (70, 5) + + CMS.unlock() + move_info = [] + psize=(0.35,0.35) + + + Iref_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Iref_label.add_label(text="IREF", layer=met2_label) + move_info.append((Iref_label, CMS.ports["refport_N"], None)) + + Icopy_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Icopy_label.add_label(text="ICOPY", layer=met2_label) + move_info.append((Icopy_label, CMS.ports["copyport_N"], None)) + + # VSS/VDD label (for sources/bulk connection) + if transistor_type.lower() == "nfet": + bulk_net_name = "VSS" + else: # pfet + bulk_net_name = "VDD" + + bulk_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + bulk_label.add_label(text=bulk_net_name, layer=met2_label) + move_info.append((bulk_label, CMS.ports["bulkport_N"], None)) + + # Add labels to the component + for label, port, alignment in move_info: + if port: + alignment = ('c', 'b') if alignment is None else alignment + aligned_label = align_comp_to_port(label, port, alignment=alignment) + CMS.add(aligned_label) + + return CMS.flatten() + + +## To Test their primitives +# from current_mirror import current_mirror, current_mirror_netlist + +if __name__ == "__main__": + comp = current_mirror_base(sky130, num_cols=4, Width=3, device='nfet',show_netlist=False) + # comp.pprint_ports() + comp = sky130_add_current_mirror_labels(comp, transistor_type='nfet', pdk=sky130) + + + # # # Write the current mirror layout to a GDS file + comp.name = "CM" + # # delete_files_in_directory("GDS/") + # # tmpdirname = Path("GDS/").resolve() + # # delete_files_in_directory("GDS/") + # # tmp_gds_path = Path(comp.write_gds(gdsdir=tmpdirname)).resolve() + # comp.write_gds("./CM.gds") + comp.show() + # #Generate the netlist for the current mirror + # print("\n...Generating Netlist...") + # print(comp.info["netlist"].generate_netlist()) + # # # DRC Checks + # # #delete_files_in_directory("DRC/") + print("\n...Running DRC...") + drc_result = sky130.drc_magic(comp, "CM") + # #drc_result = sky130.drc_magic(comp, "CM",output_file="DRC/") + # print(drc_result['result_str']) + # # # LVS Checks + # # #delete_files_in_directory("LVS/") + print("\n...Running LVS...") + netgen_lvs_result = sky130.lvs_netgen(comp, "CM") + # # #netgen_lvs_result = sky130.lvs_netgen(comp, "CM",output_file_path="LVS/") + # # # print(netgen_lvs_result['result_str']) + diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/Regulated_cascode_CM_primitive.png b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/Regulated_cascode_CM_primitive.png new file mode 100644 index 000000000..f81a106eb Binary files /dev/null and b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/Regulated_cascode_CM_primitive.png differ diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/Self-Biased_CM_primitive.png b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/Self-Biased_CM_primitive.png new file mode 100644 index 000000000..b38885ae3 Binary files /dev/null and b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/Self-Biased_CM_primitive.png differ diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/__init__.py b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/__init__.py new file mode 100644 index 000000000..57fc9cf95 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/__init__.py @@ -0,0 +1,3 @@ +from glayout.flow.blocks.composite.regulated_cascoded_current_mirror.regulated_cascoded_current_mirror import CurrentMirror, generate_current_mirror_netlist,sky130_add_current_mirror_labels +from glayout.flow.blocks.elementary.cascode_common_source.cascode_common_source import cascode_common_source, cascode_common_source_netlist +from glayout.flow.blocks.elementary.cascode_common_source.cascode_common_source import regulated_cascode, regulated_cascode_netlist diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/helper.py b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/helper.py new file mode 100644 index 000000000..aabd21575 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/helper.py @@ -0,0 +1,17 @@ +from os import path, rename, environ , listdir, remove, chmod + +def delete_files_in_directory(directory_path): + try: + files = listdir(directory_path) + for file in files: + file_path = path.join(directory_path, file) + if path.isfile(file_path): + try: + chmod(file_path, 0o777) + remove(file_path) + except PermissionError: + print(f"PermissionError: Operation not permitted for {file_path}") + print("All files deleted successfully.") + except OSError: + print("Error occurred while deleting files.") + diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/regulated_cascode_cm.py b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/regulated_cascode_cm.py new file mode 100644 index 000000000..f2f6ae074 --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/regulated_cascode_cm.py @@ -0,0 +1,402 @@ +import sys +from os import path, rename, environ , listdir, remove, chmod +# environ['OPENBLAS_NUM_THREADS'] = '1' +from pathlib import Path +# # path to glayout +# sys.path.append(path.join(str(Path(__file__).resolve().parents[2]))) + +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as sky130 +from glayout.flow.pdk.gf180_mapped import gf180_mapped_pdk as gf180 + +from glayout.flow.primitives.guardring import tapring +from glayout.flow.routing.smart_route import smart_route +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.routing.c_route import c_route +from glayout.flow.routing.L_route import L_route +from glayout.flow.primitives.via_gen import via_stack +from glayout.flow.primitives.via_gen import via_stack + + +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized + +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, prec_array, movey, align_comp_to_port, prec_ref_center +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 gdsfactory.components import text_freetype, rectangle +from gdsfactory import Component +from gdsfactory.routing.route_quad import route_quad +from glayout.flow.spice.netlist import Netlist +from typing import Optional, Union + + +global PDK_ROOT +if 'PDK_ROOT' in environ: + PDK_ROOT = str(Path(environ['PDK_ROOT']).resolve()) +else: + PDK_ROOT = "/usr/bin/miniconda3/share/pdk/" + +from CM_primitive import current_mirror_base, generate_current_mirror_netlist + +def generate_self_biased_current_mirror_netlist( + names: str = "SelfBiasedCurrentMirror", + regulator: Component = None, + base: Component = None, + show_netlist : Optional[bool] = False, + ) -> Netlist: + """Generate a netlist for a current mirror.""" + + topnet = Netlist( + circuit_name=names, + nodes=['IREF', 'ICOPY', 'VSS'], + ) + + base_ref = topnet.connect_netlist( + base.info['netlist'], + [('VSS', 'VSS') ] + ) + + regulator_ref = topnet.connect_netlist( + regulator.info['netlist'], + [('IREF', 'IREF'), ('ICOPY', 'ICOPY'), ('VSS', 'VSS')] + ) + + topnet.connect_subnets( + base_ref, + regulator_ref, + [('IREF', 'INTA'), (('ICOPY', 'INTB')),('VSS', 'VSS')] + ) + + if show_netlist: + generated_netlist_for_lvs = topnet.generate_netlist() + print(f"Generated netlist :\n", generated_netlist_for_lvs) + + file_path_local_storage = "./gen_netlist.txt" + try: + with open(file_path_local_storage, 'w') as file: + file.write(generated_netlist_for_lvs) + except: + print(f"Verify the file availability and type: ", generated_netlist_for_lvs, type(generated_netlist_for_lvs)) + return topnet + +# @validate_arguments +def self_biased_cascode_current_mirror( + pdk: MappedPDK, + Width: float = 1, + Length: Optional[float] = None, + num_cols: int = 2, + fingers: int = 1, + type: Optional[str] = 'nfet', + with_substrate_tap: Optional[bool] = False, + with_tie: Optional[bool] = True, + with_dummy: Optional[bool] = True, + tie_layers: tuple[str,str]=("met2","met1"), + show_netlist: Optional[bool] = False, + **kwargs + ) -> Component: + """An instantiable self biased casoded current mirror that returns a Component object.""" + + pdk.activate() + maxmet_sep = pdk.util_max_metal_seperation() + psize=(0.35,0.35) + + # Create the current mirror component + SBCurrentMirror = Component(name="SBCurrentMirror") + Length = Length if Length is not None else pdk.get_grule('poly')['min_width'] + + # Create the interdigitized fets + if type.lower() =="pfet" or type.lower() =="pmos": + currm= two_pfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers, dummy=with_dummy,with_substrate_tap=with_substrate_tap,with_tie=with_tie) + well, sdglayer = "nwell", "p+s/d" + elif type.lower() =="nfet" or type.lower() =="nmos": + currm= two_nfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers,dummy=with_dummy, with_substrate_tap=with_substrate_tap,with_tie=with_tie) + well, sdglayer = "pwell", "n+s/d" + else: + raise ValueError("type must be either nfet or pfet") + + # Add the interdigitized fets to the current mirror top component + top_currm_ref = prec_ref_center(currm) + mid_currm_ref = prec_ref_center(currm) + bottom_currm_ref = prec_ref_center(currm) + + + SBCurrentMirror.add(top_currm_ref) + SBCurrentMirror.add_ports(top_currm_ref.get_ports_list(),prefix="top_currm_") + + #Routing + viam2m3 = via_stack(pdk, "met2", "met3", centered=True) + topA_drain_via = SBCurrentMirror << viam2m3 + topA_drain_via.move(SBCurrentMirror.ports[f"top_currm_A_{num_cols-1:d}_drain_E"].center).movex(1.2*(num_cols)) + topA_source_via = SBCurrentMirror << viam2m3 + topA_source_via.move(SBCurrentMirror.ports[f"top_currm_A_0_source_W"].center).movex(-2.0) + topA_gate_via = SBCurrentMirror << viam2m3 + topA_gate_via.move(SBCurrentMirror.ports[f"top_currm_A_{num_cols-1:d}_gate_E"].center).movex(-0.5*Length+maxmet_sep+0.5*(num_cols)) + + topB_drain_via = SBCurrentMirror << viam2m3 + topB_drain_via.move(SBCurrentMirror.ports[f"top_currm_B_{num_cols-1:d}_drain_E"].center).movex(+0.5*num_cols) + topB_source_via = SBCurrentMirror << viam2m3 + topB_source_via.move(topA_source_via.center).movex(-2).movey(+1+maxmet_sep-Length) + + topB_gate_via = SBCurrentMirror << viam2m3 + topB_gate_via.move(SBCurrentMirror.ports[f"top_currm_B_0_gate_W"].center).movex(-1.0) + ##################### TOP + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["A_0_drain_E"], topA_drain_via.ports["bottom_met_W"]) + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["B_0_drain_E"], topB_drain_via.ports["bottom_met_W"]) + ##################### + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["A_0_source_E"], topA_source_via.ports["bottom_met_W"]) + #SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["B_0_source_E"], topB_source_via.ports["bottom_met_W"]) + topbulk=SBCurrentMirror << straight_route(pdk,SBCurrentMirror.ports[f"top_currm_B_0_source_W"],SBCurrentMirror.ports["top_currm_welltie_W_top_met_E"]) + SBCurrentMirror << straight_route(pdk,topbulk.ports["route_W"], topB_source_via["bottom_met_E"]) + ##################### + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["A_0_gate_W"], topA_gate_via.ports["bottom_met_W"]) + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["B_0_gate_W"], topB_gate_via.ports["bottom_met_W"]) + ##################### + ASBG_shortT = SBCurrentMirror << c_route(pdk, topA_source_via.ports["top_met_W"], topB_gate_via.ports["top_met_W"], extension=0.2*max(Width,Width), width1=psize[0], width2=psize[0], cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met3") + AGBD_shortT = SBCurrentMirror << c_route(pdk, topA_gate_via.ports["top_met_E"], topB_drain_via.ports["top_met_E"], extension=0.1*max(Width,Width), width1=psize[0], width2=psize[0], cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met3") + ####################################################################################################################### + ## Adding the MID Current Mirror + mid_currm_ref.move(top_currm_ref.center).movey(-evaluate_bbox(top_currm_ref)[1]) + SBCurrentMirror.add(mid_currm_ref) + SBCurrentMirror.add_ports(mid_currm_ref.get_ports_list(), prefix="mid_cm_") + ##################### + midA_drain_via = SBCurrentMirror << viam2m3 + midA_drain_via.move(SBCurrentMirror.ports[f"mid_cm_A_0_drain_E"].center).movex(-2.0) + SBCurrentMirror << straight_route(pdk,SBCurrentMirror.ports[f"mid_cm_A_{num_cols-1:d}_drain_E"], midA_drain_via["bottom_met_W"]) + SBCurrentMirror << L_route(pdk,midA_drain_via.ports["top_met_W"], ASBG_shortT.ports["con_S"]) + #, extension=1.5*max(Width,Width), width1=psize[0], width2=psize[0], cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met3") + + midA_gate_via = SBCurrentMirror << viam2m3 + midA_gate_via.move(SBCurrentMirror.ports[f"mid_cm_A_{num_cols-1:d}_gate_E"].center).movex(1.2*(num_cols)) + SBCurrentMirror << straight_route(pdk,SBCurrentMirror.ports[f"mid_cm_A_{num_cols-1:d}_gate_E"], midA_gate_via["bottom_met_W"]) + gate_drain=SBCurrentMirror << c_route(pdk,midA_gate_via.ports["top_met_E"],topA_drain_via["top_met_E"],extension=0.1*max(Width,Width), width1=psize[0], width2=psize[0], cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met3") + + ##################### + midA_source_via = SBCurrentMirror << viam2m3 + midA_source_via.move(SBCurrentMirror.ports[f"mid_cm_A_0_source_W"].center).movex(-4.0) + midbulk1=SBCurrentMirror << straight_route(pdk,SBCurrentMirror.ports[f"mid_cm_A_0_source_W"],SBCurrentMirror.ports["mid_cm_welltie_W_top_met_E"]) + SBCurrentMirror << straight_route(pdk,midbulk1.ports["route_W"], midA_source_via["bottom_met_E"]) + SBCurrentMirror << straight_route(pdk,midA_source_via.ports["top_met_N"], topB_source_via["top_met_S"]) + + #midB_source_via = SBCurrentMirror << viam2m3 + #midB_source_via.move(SBCurrentMirror.ports[f"mid_cm_B_0_source_W"].center).movex(-5.0) + midbulk2=SBCurrentMirror << straight_route(pdk,SBCurrentMirror.ports[f"mid_cm_B_0_source_W"],SBCurrentMirror.ports["mid_cm_welltie_W_top_met_E"]) + #SBCurrentMirror << straight_route(pdk,midbulk2.ports["route_S"], midbulk1.ports["route_N"]) + + ##################### + midB_drain_via = SBCurrentMirror << viam2m3 + midB_drain_via.move(SBCurrentMirror.ports[f"mid_cm_B_0_drain_E"].center).movex(-0.5*num_cols + maxmet_sep) + SBCurrentMirror << straight_route(pdk,SBCurrentMirror.ports[f"mid_cm_B_0_drain_E"], midB_drain_via["bottom_met_W"]) + + # ####################################################################################################################### + bottom_currm_ref.move(top_currm_ref.center).movey(-2*evaluate_bbox(top_currm_ref)[1]) + SBCurrentMirror.add(bottom_currm_ref) + + SBCurrentMirror.add_ports(bottom_currm_ref.get_ports_list(),prefix="bottom_currm_") + + + #Routing + bottomA_drain_via = SBCurrentMirror << viam2m3 + bottomA_drain_via.move(SBCurrentMirror.ports[f"bottom_currm_A_{num_cols-1:d}_drain_E"].center).movex(1.2*(num_cols)) + bottomA_source_via = SBCurrentMirror << viam2m3 + bottomA_source_via.move(SBCurrentMirror.ports[f"bottom_currm_A_0_source_W"].center).movex(-2.0) + bottomA_gate_via = SBCurrentMirror << viam2m3 + bottomA_gate_via.move(SBCurrentMirror.ports[f"bottom_currm_A_{num_cols-1:d}_gate_E"].center).movex(-0.5*Length+maxmet_sep+0.5*(num_cols)) + + bottomB_drain_via = SBCurrentMirror << viam2m3 + bottomB_drain_via.move(SBCurrentMirror.ports[f"bottom_currm_B_{num_cols-1:d}_drain_E"].center).movex(+0.5*num_cols) + bottomB_source_via = SBCurrentMirror << viam2m3 + bottomB_source_via.move(bottomA_source_via.center).movex(-2).movey(+1+maxmet_sep-Length) + + bottomB_gate_via = SBCurrentMirror << viam2m3 + bottomB_gate_via.move(SBCurrentMirror.ports[f"bottom_currm_B_0_gate_W"].center).movex(-1.0) + ##################### BOTTOM + SBCurrentMirror << straight_route(pdk,bottom_currm_ref.ports["A_0_drain_E"], bottomA_drain_via.ports["bottom_met_W"]) + SBCurrentMirror << straight_route(pdk,bottom_currm_ref.ports["B_0_drain_E"], bottomB_drain_via.ports["bottom_met_W"]) + ##################### + SBCurrentMirror << straight_route(pdk,bottom_currm_ref.ports["A_0_source_E"], bottomA_source_via.ports["bottom_met_W"]) + #SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["B_0_source_E"], topB_source_via.ports["bottom_met_W"]) + bottombulk=SBCurrentMirror << straight_route(pdk,SBCurrentMirror.ports[f"bottom_currm_B_0_source_W"],SBCurrentMirror.ports["bottom_currm_welltie_W_top_met_E"]) + SBCurrentMirror << straight_route(pdk,bottombulk.ports["route_W"], bottomB_source_via["bottom_met_E"]) + SBCurrentMirror << straight_route(pdk,midA_source_via.ports["top_met_S"], bottomB_source_via["top_met_N"]) + + ##################### + SBCurrentMirror << straight_route(pdk,bottom_currm_ref.ports["A_0_gate_W"], bottomA_gate_via.ports["bottom_met_W"]) + SBCurrentMirror << straight_route(pdk,bottom_currm_ref.ports["B_0_gate_W"], bottomB_gate_via.ports["bottom_met_W"]) + # ##################### + ASBG_shortB = SBCurrentMirror << c_route(pdk, bottomA_source_via.ports["top_met_W"], bottomB_gate_via.ports["top_met_W"], extension=0.2*max(Width,Width), width1=psize[0], width2=psize[0], cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met3") + AGBD_shortB = SBCurrentMirror << c_route(pdk, bottomA_gate_via.ports["top_met_E"], bottomB_drain_via.ports["top_met_E"], extension=0.1*max(Width,Width), width1=psize[0], width2=psize[0], cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met3") + + SBCurrentMirror << L_route(pdk,midB_drain_via.ports["top_met_S"], bottomA_source_via.ports["top_met_E"]) + ####################################################################################################################### + ####################################################################################################################### + # # Connecting dummies to the welltie + # if with_dummy: + # try: + # CurrentMirror << straight_route(pdk, CurrentMirror.ports["A_0_dummy_L_gsdcon_top_met_W"],CurrentMirror.ports["welltie_W_top_met_W"],glayer2="met1") + # except KeyError: + # pass + # try: + # end_col = num_cols - 1 + # port1 = f'B_{end_col}_dummy_R_gdscon_top_met_E' + # CurrentMirror << straight_route(pdk, CurrentMirror.ports[port1], CurrentMirror.ports["welltie_E_top_met_E"], glayer2="met1") + # except KeyError: + # pass + + # # add well + # CurrentMirror.add_padding(default=pdk.get_grule(well, "active_tap")["min_enclosure"],layers=[pdk.get_glayer(well)]) + # CurrentMirror = add_ports_perimeter(CurrentMirror, layer = pdk.get_glayer(well), prefix="well_") + #if well == "nwell": + # CurrentMirror.add_padding(layers=(pdk.get_glayer("nwell"),),default= 1 ) + + ########################################################### + Irefpin = SBCurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Irefpin.move(topA_drain_via.center).movey(0.2*evaluate_bbox(top_currm_ref)[0]) + SBCurrentMirror << L_route(pdk, gate_drain.ports["con_N"],Irefpin.ports["e3"]) + + Icopypin = SBCurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Icopypin.move(topA_drain_via.center).movex(+0.5*num_cols).movey(0.2*evaluate_bbox(top_currm_ref)[0]) + SBCurrentMirror << L_route(pdk, bottomA_drain_via["top_met_E"],Icopypin.ports["e4"]) + + Ibiaspin = SBCurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Ibiaspin.move(AGBD_shortB.center).movey(-evaluate_bbox(top_currm_ref)[1]) + SBCurrentMirror << L_route(pdk, AGBD_shortB["con_S"],Ibiaspin.ports["e1"]) + SBCurrentMirror << c_route(pdk, topB_drain_via["top_met_W"],Ibiaspin.ports["e1"],extension=max(Width,Width), width1=psize[0], width2=psize[0], cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met3") + + + bulkpin = SBCurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + bulkpin.move(topB_source_via.center).movey(0.3*evaluate_bbox(top_currm_ref)[1]) + SBCurrentMirror << straight_route(pdk,topB_source_via["top_met_N"], bulkpin.ports["e4"] ) + + SBCurrentMirror.add_ports(Irefpin.get_ports_list(), prefix="refport_") + SBCurrentMirror.add_ports(Icopypin.get_ports_list(), prefix="copyport_") + SBCurrentMirror.add_ports(Ibiaspin.get_ports_list(), prefix="biasport_") + SBCurrentMirror.add_ports(bulkpin.get_ports_list(), prefix="bulkport_") + + ############################## + # # Adding the Top Current Mirror Netlist + # topcurrm.info["netlist"] = generate_current_mirror_netlist( + # pdk=pdk, + # instance_name="TopCurrentMirror", + # CM_size= (Width, Length, num_cols,fingers), # (width, length, multipliers, fingers) + # transistor_type=type, + # drain_net_ref="IREF", # Input drain connected to VREF + # drain_net_copy="ICOPY", # Output drain connected to VCOPY + # gate_net="IREF", # Gate connected to VREF + # source_net_ref="INTA" , + # source_net_copy="INTB" , + # proposed_ground= "VSS" if type=="nfet" else "VDD", #Proposed ground should also change + # subckt_only=True, + # show_netlist=False, + # ) + + # SBCurrentMirror.info["netlist"] = generate_self_biased_current_mirror_netlist( + # names=SBCurrentMirror.name, + # regulator=topcurrm, + # base=BCM, + # show_netlist=False, + # ) + + return rename_ports_by_orientation(component_snap_to_grid(SBCurrentMirror)) + +def add_self_biased_cascode_cm_labels( + CMS: Component, + transistor_type: str = "nfet", + pdk: MappedPDK =sky130 + ) -> Component: + """Add labels to the current mirror layout for LVS, handling both nfet and pfet.""" + + #Would be adjusted for pdk agonastic later + met2_pin = (69, 16) + met2_label = (69, 5) + met3_pin = (70, 16) + met3_label = (70, 5) + + CMS.unlock() + move_info = [] + psize=(0.35,0.35) + + + # VREF label (for both gate and drain of transistor A, and dummy drains) + Iref_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Iref_label.add_label(text="IREF", layer=met2_label) + move_info.append((Iref_label, CMS.ports["refport_N"], None)) # Drain of A + #move_info.append((Iref_label, CMS.ports["gateshortports_con_N"], None)) # Gate of A & B + + # VCOPY label (for drain of transistor B) + Icopy_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Icopy_label.add_label(text="ICOPY", layer=met2_label) + move_info.append((Icopy_label, CMS.ports["copyport_N"], None)) # Drain of B + + # VCOPY label (for drain of transistor B) + Ibias_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Ibias_label.add_label(text="IBIAS", layer=met2_label) + move_info.append((Ibias_label, CMS.ports["biasport_N"], None)) # Drain of B + + + # VSS/VDD label (for sources/bulk connection) + if transistor_type.lower() == "nfet": + bulk_net_name = "VSS" + bulk_pin_layer = met2_pin + bulk_label_layer = met2_label + else: # pfet + bulk_net_name = "VDD" + bulk_pin_layer = met2_pin + bulk_label_layer = met2_label + + ##Need to clarify the bulk and source connection?? + # VB label + vb_label = rectangle(layer=bulk_pin_layer, size=psize, centered=True).copy() + vb_label.add_label(text=bulk_net_name , layer=bulk_label_layer) + move_info.append((vb_label, CMS.ports["bulkport_N"], None)) + + # Add labels to the component + for label, port, alignment in move_info: + if port: + alignment = ('c', 'b') if alignment is None else alignment + aligned_label = align_comp_to_port(label, port, alignment=alignment) + CMS.add(aligned_label) + + return CMS.flatten() + + + + +## To Test their primitives +# from current_mirror import current_mirror, current_mirror_netlist + +if __name__ == "__main__": + # Main function to generate the current mirror layout + # mappedpdk, Width, Length, num_cols, fingers, transistor type + comp = self_biased_cascode_current_mirror(sky130, num_cols=2, Width=3, device='nfet',show_netlist=False) + #comp.pprint_ports() + comp = add_self_biased_cascode_cm_labels(comp, transistor_type='nfet', pdk=sky130) + + + + + # # # Write the current mirror layout to a GDS file + #comp.name = "CM" + # # delete_files_in_directory("GDS/") + # # tmpdirname = Path("GDS/").resolve() + # # delete_files_in_directory("GDS/") + # # tmp_gds_path = Path(comp.write_gds(gdsdir=tmpdirname)).resolve() + #comp.write_gds("./CM.gds") + comp.show() + # # # Generate the netlist for the current mirror + # # print("\n...Generating Netlist...") + #print(comp.info["netlist"].generate_netlist()) + # # # DRC Checks + # # #delete_files_in_directory("DRC/") + print("\n...Running DRC...") + drc_result = sky130.drc_magic(comp, "CM") + # # #drc_result = sky130.drc_magic(comp, "CM",output_file="DRC/") + # print(drc_result['result_str']) + # # LVS Checks + # #delete_files_in_directory("LVS/") + #print("\n...Running LVS...") + #netgen_lvs_result = sky130.lvs_netgen(comp, "CM") + #netgen_lvs_result = sky130.lvs_netgen(comp, "CM",output_file_path="LVS/") + #print(netgen_lvs_result['result_str']) + + \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/self-biased_cascode_cm.py b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/self-biased_cascode_cm.py new file mode 100644 index 000000000..04c9adcbc --- /dev/null +++ b/openfasoc/generators/glayout/glayout/flow/blocks/composite/regulated_cascoded_current_mirror/self-biased_cascode_cm.py @@ -0,0 +1,341 @@ +import sys +from os import path, rename, environ , listdir, remove, chmod +# environ['OPENBLAS_NUM_THREADS'] = '1' +from pathlib import Path +# # path to glayout +# sys.path.append(path.join(str(Path(__file__).resolve().parents[2]))) + +from glayout.flow.pdk.mappedpdk import MappedPDK +from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk as sky130 +from glayout.flow.pdk.gf180_mapped import gf180_mapped_pdk as gf180 + +from glayout.flow.primitives.guardring import tapring +from glayout.flow.routing.smart_route import smart_route +from glayout.flow.routing.straight_route import straight_route +from glayout.flow.routing.c_route import c_route +from glayout.flow.routing.L_route import L_route +from glayout.flow.primitives.via_gen import via_stack +from glayout.flow.primitives.via_gen import via_stack + + +from glayout.flow.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized + +from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid +from glayout.flow.pdk.util.comp_utils import evaluate_bbox, prec_center, prec_array, movey, align_comp_to_port, prec_ref_center +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 gdsfactory.components import text_freetype, rectangle +from gdsfactory import Component +from gdsfactory.routing.route_quad import route_quad +from glayout.flow.spice.netlist import Netlist +from typing import Optional, Union + + +global PDK_ROOT +if 'PDK_ROOT' in environ: + PDK_ROOT = str(Path(environ['PDK_ROOT']).resolve()) +else: + PDK_ROOT = "/usr/bin/miniconda3/share/pdk/" + +from CM_primitive import current_mirror_base, generate_current_mirror_netlist + +def generate_self_biased_current_mirror_netlist( + names: str = "SelfBiasedCurrentMirror", + regulator: Component = None, + base: Component = None, + show_netlist : Optional[bool] = False, + ) -> Netlist: + """Generate a netlist for a current mirror.""" + + topnet = Netlist( + circuit_name=names, + nodes=['IREF', 'ICOPY', 'VSS'], + ) + + base_ref = topnet.connect_netlist( + base.info['netlist'], + [('VSS', 'VSS') ] + ) + + regulator_ref = topnet.connect_netlist( + regulator.info['netlist'], + [('IREF', 'IREF'), ('ICOPY', 'ICOPY'), ('VSS', 'VSS')] + ) + + topnet.connect_subnets( + base_ref, + regulator_ref, + [('IREF', 'INTA'), (('ICOPY', 'INTB')),('VSS', 'VSS')] + ) + + if show_netlist: + generated_netlist_for_lvs = topnet.generate_netlist() + print(f"Generated netlist :\n", generated_netlist_for_lvs) + + file_path_local_storage = "./gen_netlist.txt" + try: + with open(file_path_local_storage, 'w') as file: + file.write(generated_netlist_for_lvs) + except: + print(f"Verify the file availability and type: ", generated_netlist_for_lvs, type(generated_netlist_for_lvs)) + return topnet + +# @validate_arguments +def self_biased_cascode_current_mirror( + pdk: MappedPDK, + Width: float = 1, + Length: Optional[float] = None, + num_cols: int = 2, + fingers: int = 1, + type: Optional[str] = 'nfet', + with_substrate_tap: Optional[bool] = False, + with_tie: Optional[bool] = True, + with_dummy: Optional[bool] = True, + tie_layers: tuple[str,str]=("met2","met1"), + show_netlist: Optional[bool] = False, + **kwargs + ) -> Component: + """An instantiable self biased casoded current mirror that returns a Component object.""" + + pdk.activate() + maxmet_sep = pdk.util_max_metal_seperation() + psize=(0.35,0.35) + + # Create the current mirror component + SBCurrentMirror = Component(name="SBCurrentMirror") + Length = Length if Length is not None else pdk.get_grule('poly')['min_width'] + + # Create the interdigitized fets + if type.lower() =="pfet" or type.lower() =="pmos": + top_currm= two_pfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers, dummy=with_dummy,with_substrate_tap=with_substrate_tap,with_tie=with_tie) + well, sdglayer = "nwell", "p+s/d" + elif type.lower() =="nfet" or type.lower() =="nmos": + top_currm= two_nfet_interdigitized(pdk,numcols=num_cols,width=Width,length=Length,fingers=fingers,dummy=with_dummy, with_substrate_tap=with_substrate_tap,with_tie=with_tie) + well, sdglayer = "pwell", "n+s/d" + else: + raise ValueError("type must be either nfet or pfet") + + # Add the interdigitized fets to the current mirror top component + top_currm_ref = prec_ref_center(top_currm) + SBCurrentMirror.add(top_currm_ref) + SBCurrentMirror.add_ports(top_currm_ref.get_ports_list(),prefix="top_currm_") + + #Routing + viam2m3 = via_stack(pdk, "met2", "met3", centered=True) + topA_drain_via = SBCurrentMirror << viam2m3 + topA_drain_via.move(SBCurrentMirror.ports[f"top_currm_A_{num_cols-1:d}_drain_E"].center).movex(0.5*(num_cols)) + topA_source_via = SBCurrentMirror << viam2m3 + topA_source_via.move(SBCurrentMirror.ports[f"top_currm_A_0_source_W"].center).movex(-1) + topA_gate_via = SBCurrentMirror << viam2m3 + topA_gate_via.move(SBCurrentMirror.ports[f"top_currm_A_{num_cols-1:d}_gate_E"].center).movex(-0.5*Length+maxmet_sep+0.5*(num_cols)) + + topB_drain_via = SBCurrentMirror << viam2m3 + topB_drain_via.move(topA_drain_via.center).movex(+0.5*num_cols).movey(+1+maxmet_sep-Length) + topB_source_via = SBCurrentMirror << viam2m3 + topB_source_via.move(topA_source_via.center).movex(-1+Length).movey(+1+maxmet_sep-Length) + topB_gate_via = SBCurrentMirror << viam2m3 + topB_gate_via.move(topA_gate_via.center).movey(-1.0-0.4*maxmet_sep) + ##################### + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["A_0_drain_E"], topA_drain_via.ports["bottom_met_W"]) + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["B_0_drain_E"], topB_drain_via.ports["bottom_met_W"]) + ##################### + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["A_0_source_E"], topA_source_via.ports["bottom_met_W"]) + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["B_0_source_E"], topB_source_via.ports["bottom_met_W"]) + + #source_short = SBCurrentMirror << straight_route(pdk, topA_source_via.ports["top_met_N"], topB_source_via.ports["top_met_S"]) + #,extension=1.2*max(Width,Width), width1=psize[0], width2=ps, cwidth=0.32, e1glayer="met3", e2glayer="met3", cglayer="met2") + ##################### + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["A_0_gate_W"], topA_gate_via.ports["bottom_met_W"]) + SBCurrentMirror << straight_route(pdk,top_currm_ref.ports["B_0_gate_W"], topB_gate_via.ports["bottom_met_W"]) + ##################### + gate_short = SBCurrentMirror << straight_route(pdk, topA_gate_via.ports["top_met_S"], topB_gate_via.ports["top_met_N"]) + + # #connecting the Drian of A to gate short + SBCurrentMirror << straight_route(pdk,topA_drain_via.ports["top_met_S"],gate_short.ports["route_N"]) + + # # Connecting dummies to the welltie + # if with_dummy: + # try: + # CurrentMirror << straight_route(pdk, CurrentMirror.ports["A_0_dummy_L_gsdcon_top_met_W"],CurrentMirror.ports["welltie_W_top_met_W"],glayer2="met1") + # except KeyError: + # pass + # try: + # end_col = num_cols - 1 + # port1 = f'B_{end_col}_dummy_R_gdscon_top_met_E' + # CurrentMirror << straight_route(pdk, CurrentMirror.ports[port1], CurrentMirror.ports["welltie_E_top_met_E"], glayer2="met1") + # except KeyError: + # pass + + # # add well + # CurrentMirror.add_padding(default=pdk.get_grule(well, "active_tap")["min_enclosure"],layers=[pdk.get_glayer(well)]) + # CurrentMirror = add_ports_perimeter(CurrentMirror, layer = pdk.get_glayer(well), prefix="well_") + #if well == "nwell": + # CurrentMirror.add_padding(layers=(pdk.get_glayer("nwell"),),default= 1 ) + + + ## Adding the Bottom Current Mirror + BCM=SBCurrentMirror << current_mirror_base(pdk=pdk, Width=Width, Length=Length, num_cols=num_cols, fingers=fingers, type=type, **kwargs) + bottom_cm_ref= prec_ref_center(BCM) + bottom_cm_ref.move(top_currm_ref.center).movey(-evaluate_bbox(top_currm_ref)[1]) + + #bottom_cm_ref.pprint_ports() + + SBCurrentMirror.add(bottom_cm_ref) + SBCurrentMirror.add_ports(bottom_cm_ref.get_ports_list(), prefix="bottom_cm_") + + ############################## + + SBCurrentMirror << straight_route(pdk,topA_source_via.ports["top_met_S"], bottom_cm_ref.ports["refport_N"]) + SBCurrentMirror << straight_route(pdk,topB_source_via.ports["top_met_S"], bottom_cm_ref.ports["copyport_N"]) + src2bulk=SBCurrentMirror << straight_route(pdk, bottom_cm_ref.ports["bulkport_N"],SBCurrentMirror.ports["top_currm_welltie_S_top_met_S"], glayer2="met2") + + ############################## + ########################################################### + Irefpin = SBCurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Irefpin.move(topA_drain_via.center).movey(0.2*evaluate_bbox(top_currm_ref)[0]) + SBCurrentMirror << straight_route(pdk, topA_drain_via.ports["top_met_N"],Irefpin.ports["e4"], glayer2="met3") + + Icopypin = SBCurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + Icopypin.move(topA_drain_via.center).movex(+0.5*num_cols).movey(0.2*evaluate_bbox(top_currm_ref)[0]) + SBCurrentMirror << straight_route(pdk, topB_drain_via.ports["top_met_N"],Icopypin.ports["e4"], glayer2="met3") + + #bulkpin = SBCurrentMirror << rectangle(size=psize,layer=pdk.get_glayer("met3"),centered=True) + #bulkpin.move(src2bulk.center).movey(0.2*evaluate_bbox(top_currm_ref)[0]) + #CurrentMirror << straight_route(pdk, src2bulk["route_N"],bulkpin.ports["e4"], glayer2="met3") + ########################################################### + + + ############################## + # # Adding the Top Current Mirror Netlist + # topcurrm.info["netlist"] = generate_current_mirror_netlist( + # pdk=pdk, + # instance_name="TopCurrentMirror", + # CM_size= (Width, Length, num_cols,fingers), # (width, length, multipliers, fingers) + # transistor_type=type, + # drain_net_ref="IREF", # Input drain connected to VREF + # drain_net_copy="ICOPY", # Output drain connected to VCOPY + # gate_net="IREF", # Gate connected to VREF + # source_net_ref="INTA" , + # source_net_copy="INTB" , + # proposed_ground= "VSS" if type=="nfet" else "VDD", #Proposed ground should also change + # subckt_only=True, + # show_netlist=False, + # ) + + # SBCurrentMirror.info["netlist"] = generate_self_biased_current_mirror_netlist( + # names=SBCurrentMirror.name, + # regulator=topcurrm, + # base=BCM, + # show_netlist=False, + # ) + + return rename_ports_by_orientation(component_snap_to_grid(SBCurrentMirror)) + +def add_self_biased_cascode_cm_labels( + CMS: Component, + transistor_type: str = "nfet", + pdk: MappedPDK =sky130 + ) -> Component: + """Add labels to the current mirror layout for LVS, handling both nfet and pfet.""" + + #Would be adjusted for pdk agonastic later + met2_pin = (69, 16) + met2_label = (69, 5) + met3_pin = (70, 16) + met3_label = (70, 5) + + CMS.unlock() + move_info = [] + psize=(0.35,0.35) + + + # VREF label (for both gate and drain of transistor A, and dummy drains) + Iref_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Iref_label.add_label(text="IREF", layer=met2_label) + move_info.append((Iref_label, CMS.ports["refport_N"], None)) # Drain of A + #move_info.append((Iref_label, CMS.ports["gateshortports_con_N"], None)) # Gate of A & B + + # VCOPY label (for drain of transistor B) + Icopy_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + Icopy_label.add_label(text="ICOPY", layer=met2_label) + move_info.append((Icopy_label, CMS.ports["copyport_N"], None)) # Drain of B + + ################################################################################################################### + # INTA label (for source/drain connection of transistor top/bottom) + INTA_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + INTA_label.add_label(text="INTA", layer=met2_label) + move_info.append((INTA_label, CMS.ports["INTAport_N"], None)) # Drain of A + + # INTB label (for source/drain connection of transistor top/bottom) + INTB_label = rectangle(layer=met2_pin, size=psize, centered=True).copy() + INTB_label.add_label(text="INTB", layer=met2_label) + move_info.append((INTB_label, CMS.ports["INTBport_N"], None)) # Drain of B + ################################################################################################################### + + + # VSS/VDD label (for sources/bulk connection) + if transistor_type.lower() == "nfet": + bulk_net_name = "VSS" + bulk_pin_layer = met2_pin + bulk_label_layer = met2_label + else: # pfet + bulk_net_name = "VDD" + bulk_pin_layer = met2_pin + bulk_label_layer = met2_label + + ##Need to clarify the bulk and source connection?? + # VB label + vb_label = rectangle(layer=bulk_pin_layer, size=psize, centered=True).copy() + vb_label.add_label(text=bulk_net_name , layer=bulk_label_layer) + move_info.append((vb_label, CMS.ports["purposegndport_S"], None)) + + # Add labels to the component + for label, port, alignment in move_info: + if port: + alignment = ('c', 'b') if alignment is None else alignment + aligned_label = align_comp_to_port(label, port, alignment=alignment) + CMS.add(aligned_label) + + return CMS.flatten() + + + + +## To Test their primitives +# from current_mirror import current_mirror, current_mirror_netlist + +if __name__ == "__main__": + # Main function to generate the current mirror layout + # mappedpdk, Width, Length, num_cols, fingers, transistor type + comp = self_biased_cascode_current_mirror(sky130, num_cols=4, Width=3, device='nfet',show_netlist=False) + #comp.pprint_ports() + #comp = add_self_biased_cascode_cm_labels(comp, transistor_type='nfet', pdk=sky130) + + + + + # # # Write the current mirror layout to a GDS file + #comp.name = "CM" + # # delete_files_in_directory("GDS/") + # # tmpdirname = Path("GDS/").resolve() + # # delete_files_in_directory("GDS/") + # # tmp_gds_path = Path(comp.write_gds(gdsdir=tmpdirname)).resolve() + #comp.write_gds("./CM.gds") + comp.show() + # # # Generate the netlist for the current mirror + # # print("\n...Generating Netlist...") + #print(comp.info["netlist"].generate_netlist()) + # # # DRC Checks + # # #delete_files_in_directory("DRC/") + # print("\n...Running DRC...") + # drc_result = sky130.drc_magic(comp, "CM") + # # #drc_result = sky130.drc_magic(comp, "CM",output_file="DRC/") + # print(drc_result['result_str']) + # # LVS Checks + # #delete_files_in_directory("LVS/") + #print("\n...Running LVS...") + #netgen_lvs_result = sky130.lvs_netgen(comp, "CM") + #netgen_lvs_result = sky130.lvs_netgen(comp, "CM",output_file_path="LVS/") + #print(netgen_lvs_result['result_str']) + + \ No newline at end of file diff --git a/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_mapped.py b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_mapped.py index 977382d3d..4ec12a115 100644 --- a/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_mapped.py +++ b/openfasoc/generators/glayout/glayout/flow/pdk/sky130_mapped/sky130_mapped.py @@ -141,4 +141,4 @@ # set the grid size sky130_mapped_pdk.gds_write_settings.precision = 5*10**-9 sky130_mapped_pdk.cell_decorator_settings.cache=False -sky130_mapped_pdk.gds_write_settings.flatten_invalid_refs=False \ No newline at end of file +sky130_mapped_pdk.gds_write_settings.flatten_invalid_refs=False