diff --git a/src/glayout/blocks/composite/chopper_switch/CSWITCH.py b/src/glayout/blocks/composite/chopper_switch/CSWITCH.py new file mode 100644 index 00000000..833fba31 --- /dev/null +++ b/src/glayout/blocks/composite/chopper_switch/CSWITCH.py @@ -0,0 +1,315 @@ + +from glayout import MappedPDK, sky130 , gf180 +#from gdsfactory.cell import cell +from gdsfactory import Component +from gdsfactory.components import text_freetype, rectangle +from glayout import nmos, pmos +from glayout import via_stack, via_array +from glayout import rename_ports_by_orientation +from glayout import tapring +from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port +from glayout.util.port_utils import add_ports_perimeter,print_ports +from glayout.util.snap_to_grid import component_snap_to_grid +from glayout.spice.netlist import Netlist +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route + +import os +import subprocess + +# Run a shell, source .bashrc, then printenv +cmd = 'bash -c "source ~/.bashrc && printenv"' +result = subprocess.run(cmd, shell=True, text=True, capture_output=True) +env_vars = {} +for line in result.stdout.splitlines(): + if '=' in line: + key, value = line.split('=', 1) + env_vars[key] = value + +# Now, update os.environ with these +os.environ.update(env_vars) + +import sys +from pathlib import Path +sys.path.append(os.path.abspath("../elementary/TGSW")) + +from TGSW import tgswitch + +def add_cswitch_labels( + cswitch_in: Component, + pdk: MappedPDK, + ) -> Component: + + cswitch_in.unlock() + + psize=(0.5,0.5) + # list that will contain all port/comp info + move_info = list() + # create labels and append to info list + + # VSS + vsslabel = rectangle(layer=pdk.get_glayer("met3_pin"),size=psize,centered=True).copy() + vsslabel.add_label(text="VSS",layer=pdk.get_glayer("met3_label")) + move_info.append((vsslabel,cswitch_in.ports["VSS_TOP_top_met_N"],None)) + move_info.append((vsslabel,cswitch_in.ports["VSS_BOTTOM_top_met_N"],None)) + #gnd_ref = top_level << gndlabel; + + #suply + vddlabel = rectangle(layer=pdk.get_glayer("met3_pin"),size=psize,centered=True).copy() + vddlabel.add_label(text="VDD",layer=pdk.get_glayer("met3_pin")) + move_info.append((vddlabel,cswitch_in.ports["VDD_TOPL_top_met_N"],None)) + move_info.append((vddlabel,cswitch_in.ports["VDD_TOPR_top_met_N"],None)) + move_info.append((vddlabel,cswitch_in.ports["VDD_BOTTOML_top_met_S"],None)) + move_info.append((vddlabel,cswitch_in.ports["VDD_BOTTOMR_top_met_S"],None)) + #sup_ref = top_level << suplabel; + + # output + outputplabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + outputplabel.add_label(text="VOUTP",layer=pdk.get_glayer("met2_pin")) + move_info.append((outputplabel,cswitch_in.ports["OUTP_top_met_E"],None)) + outputnlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + outputnlabel.add_label(text="VOUTN",layer=pdk.get_glayer("met2_pin")) + move_info.append((outputnlabel,cswitch_in.ports["OUTN_top_met_E"],None)) + #op_ref = top_level << outputlabel; + + # input + inputplabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + inputplabel.add_label(text="VINN",layer=pdk.get_glayer("met2_pin")) + move_info.append((inputplabel,cswitch_in.ports["INP_top_met_W"], None)) + inputnlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + inputnlabel.add_label(text="VINP",layer=pdk.get_glayer("met2_pin")) + move_info.append((inputnlabel,cswitch_in.ports["INN_top_met_W"], None)) + #ip_ref = top_level << inputlabel; + + # CLK + clklabel = rectangle(layer=pdk.get_glayer("met3_pin"),size=psize,centered=True).copy() + clklabel.add_label(text="CLK",layer=pdk.get_glayer("met3_pin")) + move_info.append((clklabel,cswitch_in.ports["CLK_TOP_top_met_N"], None)) + move_info.append((clklabel,cswitch_in.ports["CLK_BOTTOM_top_met_S"], None)) + clkinvlabel = rectangle(layer=pdk.get_glayer("met3_pin"),size=psize,centered=True).copy() + clkinvlabel.add_label(text="CLKINV",layer=pdk.get_glayer("met3_pin")) + move_info.append((clkinvlabel,cswitch_in.ports["CLKINV_TOP_top_met_N"], None)) + move_info.append((clkinvlabel,cswitch_in.ports["CLKINV_BOTTOM_top_met_S"], None)) + #clk_ref = top_level << clklabel; + + for comp, prt, alignment in move_info: + alignment = ('c','b') if alignment is None else alignment + compref = align_comp_to_port(comp, prt, alignment=alignment) + cswitch_in.add(compref) + + return cswitch_in.flatten() + +def cswitch( + pdk: MappedPDK, + width: tuple[float,float] = (10,10), + length: tuple[float,float] = (0.5,0.5), + fingers: tuple[int,int] = (6,6), + multipliers: tuple[int,int] = (1,1), + dummy_1: tuple[bool,bool] = (True,True), + dummy_2: tuple[bool,bool] = (True,True), + tie_layers1: tuple[str,str] = ("met2","met1"), + tie_layers2: tuple[str,str] = ("met2","met1"), + sd_rmult: int=1, + **kwargs + ) -> Component: + + pdk.activate() + + #top level component + top_level = Component(name="cswitch") + + #four TG Switch + SW1 = tgswitch(pdk, "vertical",(width[0],width[1]),(length[0],length[1]),(fingers[0],fingers[1]),(multipliers[0],multipliers[1])) + SW2 = tgswitch(pdk, "vertical_inv",(width[0],width[1]),(length[0],length[1]),(fingers[0],fingers[1]),(multipliers[0],multipliers[1])) + SW3 = tgswitch(pdk, "vertical",(width[0],width[1]),(length[0],length[1]),(fingers[0],fingers[1]),(multipliers[0],multipliers[1])) + SW4 = tgswitch(pdk, "vertical_inv",(width[0],width[1]),(length[0],length[1]),(fingers[0],fingers[1]),(multipliers[0],multipliers[1])) + + SW1_ref = top_level << SW1 + SW2_ref = top_level << SW2 + SW3_ref = top_level << SW3 + SW4_ref = top_level << SW4 + + SW1_ref.name = "SW1" + SW2_ref.name = "SW2" + SW3_ref.name = "SW3" + SW4_ref.name = "SW4" + + x_distance = 15 + y_distance = 14.5 + ref_dimensions = evaluate_bbox(SW1_ref) + + SW2_ref.movex(SW1_ref.xmax + x_distance) + SW3_ref.movey(SW1_ref.ymin - y_distance) + SW4_ref.movex(SW1_ref.xmax + x_distance).movey(SW1_ref.ymin - y_distance) + + viam2m3 = via_stack(pdk, "met2", "met3", centered=True) #met2 is the bottom layer. met3 is the top layer. + + #via for input and output + vinp_start_via = top_level << viam2m3 + vinp_end_via = top_level << viam2m3 + vinn_start_via = top_level << viam2m3 + vinn_end_via = top_level << viam2m3 + + voutp_start_via = top_level << viam2m3 + voutp_end_via = top_level << viam2m3 + voutn_start_via = top_level << viam2m3 + voutn_end_via = top_level << viam2m3 + + #via for CLK and CLKinv + clk_top_via = top_level << viam2m3 + clk_bottom_via = top_level << viam2m3 + clkinv_top_via = top_level << viam2m3 + clkinv_bottom_via = top_level << viam2m3 + + + top_level << straight_route(pdk, SW1_ref.ports["P_gate_E"], SW2_ref.ports["N_gate_W"]) + top_level << straight_route(pdk, SW1_ref.ports["N_gate_E"], SW2_ref.ports["P_gate_W"]) + top_level << straight_route(pdk, SW3_ref.ports["P_gate_E"], SW4_ref.ports["N_gate_W"]) + top_level << straight_route(pdk, SW3_ref.ports["N_gate_E"], SW4_ref.ports["P_gate_W"]) + + vinp_start_via.move(SW1_ref.ports["P_source_top_met_W"].center).movey(SW1_ref.ymin - pdk.util_max_metal_seperation()).movex(-ref_dimensions[0]/1.55) + vinn_start_via.move(vinp_start_via.center).movey(- 0.75 - pdk.util_max_metal_seperation()) + vinp_end_via.move(vinp_start_via.center).movex(SW2_ref.xmax - x_distance/2) + vinn_end_via.move(vinn_start_via.center).movex(SW2_ref.xmax - x_distance/2 - 1) + + voutp_start_via.move(vinp_start_via.center).movey(pdk.util_max_metal_seperation()+0.75).movex(SW2_ref.xmin - 4) + voutn_start_via.move(vinn_start_via.center).movey(-pdk.util_max_metal_seperation()-0.75).movex(SW2_ref.xmin - 4) + voutp_end_via.move(voutp_start_via.center).movex(SW2_ref.xmax-6) + voutn_end_via.move(voutn_start_via.center).movex(SW2_ref.xmax-7) + + clk_top_via.move(SW1_ref.ports["P_gate_E"].center).movex(SW1_ref.xmax-1.5) + clkinv_top_via.move(SW2_ref.ports["P_gate_W"].center).movex(SW1_ref.xmin+1.5) + clk_bottom_via.move(SW3_ref.ports["P_gate_E"].center).movex(SW1_ref.xmax-1.5) + clkinv_bottom_via.move(SW4_ref.ports["P_gate_W"].center).movex(SW1_ref.xmin+1.5) + + #input routes + top_level << L_route(pdk, SW1_ref.ports["P_drain_top_met_W"], vinp_start_via.ports["top_met_N"]) + top_level << L_route(pdk, SW3_ref.ports["P_drain_top_met_W"], vinn_start_via.ports["top_met_S"]) + top_level << L_route(pdk, SW4_ref.ports["P_drain_top_met_W"], vinn_end_via.ports["top_met_N"]) + top_level << L_route(pdk, SW2_ref.ports["P_drain_top_met_W"], vinp_end_via.ports["top_met_S"]) + top_level << straight_route(pdk, vinp_start_via.ports["bottom_met_E"], vinp_end_via.ports["bottom_met_W"]) + top_level << straight_route(pdk, vinn_start_via.ports["bottom_met_E"], vinn_end_via.ports["bottom_met_W"]) + + #output routes + top_level << L_route(pdk, SW1_ref.ports["P_source_top_met_E"], voutp_start_via.ports["top_met_N"]) + top_level << L_route(pdk, SW3_ref.ports["P_source_top_met_E"], voutn_start_via.ports["top_met_S"]) + top_level << L_route(pdk, SW2_ref.ports["P_source_top_met_E"], voutn_end_via.ports["top_met_N"]) + top_level << L_route(pdk, SW4_ref.ports["P_source_top_met_E"], voutp_end_via.ports["top_met_S"]) + top_level << straight_route(pdk, voutp_start_via.ports["bottom_met_E"], voutp_end_via.ports["bottom_met_W"]) + top_level << straight_route(pdk, voutn_start_via.ports["bottom_met_E"], voutn_end_via.ports["bottom_met_W"]) + + #CLK routes + top_level << straight_route(pdk, clk_top_via.ports["top_met_S"], clk_bottom_via.ports["top_met_N"]) + top_level << straight_route(pdk, clkinv_top_via.ports["top_met_S"], clkinv_bottom_via.ports["top_met_N"]) + + # Add tapring + tap_ring = tapring(pdk, enclosed_rectangle=evaluate_bbox(top_level.flatten(), padding=pdk.get_grule("nwell", "active_diff")["min_enclosure"]+pdk.util_max_metal_seperation())) + shift_amount = -prec_center(top_level.flatten())[0] + shifty_amount = prec_center(top_level.flatten())[1] + tring_ref = top_level << tap_ring + tring_ref.movex(destination=shift_amount).movey(destination=-shifty_amount) + + #VDD Rails + viaarray = via_array(pdk, "met2", "met3", (2,1)) + + VDD1_via = top_level << viaarray + VDD2_via = top_level << viaarray + VDD3_via = top_level << viaarray + VDD4_via = top_level << viaarray + VDD5_via = top_level << viaarray + VDD6_via = top_level << viaarray + VDD7_via = top_level << viaarray + VDD8_via = top_level << viaarray + + VDD1_via.move(SW1_ref.ports["P_tie_N_top_met_E"].center).movex(SW1_ref.xmin+0.75) + VDD2_via.move(SW1_ref.ports["P_tie_S_top_met_E"].center).movex(SW1_ref.xmin+0.75) + VDD3_via.move(SW2_ref.ports["P_tie_N_top_met_E"].center).movex(SW1_ref.xmin+0.75) + VDD4_via.move(SW2_ref.ports["P_tie_S_top_met_E"].center).movex(SW1_ref.xmin+0.75) + VDD5_via.move(SW3_ref.ports["P_tie_N_top_met_E"].center).movex(SW1_ref.xmin+0.75) + VDD6_via.move(SW3_ref.ports["P_tie_S_top_met_E"].center).movex(SW1_ref.xmin+0.75) + VDD7_via.move(SW4_ref.ports["P_tie_N_top_met_E"].center).movex(SW1_ref.xmin+0.75) + VDD8_via.move(SW4_ref.ports["P_tie_S_top_met_E"].center).movex(SW1_ref.xmin+0.75) + + top_level << straight_route(pdk, VDD1_via.ports["top_met_S"], VDD6_via.ports["top_met_N"]) + top_level << straight_route(pdk, VDD4_via.ports["top_met_S"], VDD7_via.ports["top_met_N"]) + + # VSS Rails + VSS1_via = top_level << viaarray + VSS2_via = top_level << viaarray + VSS1_via.move(tring_ref.ports["N_top_met_E"].center).movex(tring_ref.xmin*2.25) + VSS2_via.move(tring_ref.ports["S_top_met_E"].center).movex(tring_ref.xmin*2.25) + + top_level << straight_route(pdk, VSS1_via.ports["top_met_N"], VSS2_via.ports["top_met_S"], width=2) + + top_level << straight_route(pdk, SW2_ref.ports["N_tie_N_top_met_W"], VSS1_via.ports["top_met_N"]) + top_level << straight_route(pdk, SW2_ref.ports["N_tie_S_top_met_W"], VSS1_via.ports["top_met_N"]) + top_level << straight_route(pdk, SW1_ref.ports["N_tie_N_top_met_E"], VSS1_via.ports["top_met_N"]) + top_level << straight_route(pdk, SW1_ref.ports["N_tie_S_top_met_E"], VSS1_via.ports["top_met_N"]) + top_level << straight_route(pdk, SW4_ref.ports["N_tie_N_top_met_W"], VSS1_via.ports["top_met_N"]) + top_level << straight_route(pdk, SW4_ref.ports["N_tie_S_top_met_W"], VSS1_via.ports["top_met_N"]) + top_level << straight_route(pdk, SW3_ref.ports["N_tie_N_top_met_E"], VSS1_via.ports["top_met_N"]) + top_level << straight_route(pdk, SW3_ref.ports["N_tie_S_top_met_E"], VSS1_via.ports["top_met_N"]) + + viam1m2 = via_stack(pdk, "met1", "met2", centered=True) #met2 is the bottom layer. met3 is the top layer. + + # tapring - bulk pmos + SW1_tapring = top_level << viam1m2 + SW2_tapring = top_level << viam1m2 + SW3_tapring = top_level << viam1m2 + SW4_tapring = top_level << viam1m2 + + SW1_tapring.move(SW1_ref.ports["N_tie_W_array_row23_col0_top_met_W"].center).movex(-1.2) + SW2_tapring.move(SW2_ref.ports["N_tie_E_array_row23_col0_top_met_W"].center).movex(1.6) + SW3_tapring.move(SW3_ref.ports["N_tie_W_array_row23_col0_top_met_W"].center).movex(-1.2) + SW4_tapring.move(SW4_ref.ports["N_tie_E_array_row23_col0_top_met_W"].center).movex(1.6) + + top_level << straight_route(pdk, SW1_tapring.ports["top_met_W"], SW1_ref.ports["N_tie_W_array_row23_col0_top_met_E"]) + top_level << straight_route(pdk, SW2_tapring.ports["top_met_E"], SW2_ref.ports["N_tie_E_array_row23_col0_top_met_W"]) + top_level << straight_route(pdk, SW3_tapring.ports["top_met_W"], SW3_ref.ports["N_tie_W_array_row23_col0_top_met_E"]) + top_level << straight_route(pdk, SW4_tapring.ports["top_met_E"], SW4_ref.ports["N_tie_E_array_row23_col0_top_met_W"]) + + top_level.add_ports(SW1_ref.get_ports_list(), prefix="SW1_") + top_level.add_ports(SW2_ref.get_ports_list(), prefix="SW2_") + top_level.add_ports(SW3_ref.get_ports_list(), prefix="SW3_") + top_level.add_ports(SW4_ref.get_ports_list(), prefix="SW4_") + + top_level.add_ports(vinp_start_via.get_ports_list(), prefix="INP_") + top_level.add_ports(vinn_start_via.get_ports_list(), prefix="INN_") + top_level.add_ports(voutp_end_via.get_ports_list(), prefix="OUTP_") + top_level.add_ports(voutn_end_via.get_ports_list(), prefix="OUTN_") + + top_level.add_ports(clk_top_via.get_ports_list(), prefix="CLK_TOP_") + top_level.add_ports(clkinv_top_via.get_ports_list(), prefix="CLKINV_TOP_") + top_level.add_ports(clk_bottom_via.get_ports_list(), prefix="CLK_BOTTOM_") + top_level.add_ports(clkinv_bottom_via.get_ports_list(), prefix="CLKINV_BOTTOM_") + + top_level.add_ports(VDD1_via.get_ports_list(), prefix="VDD_TOPL_") + top_level.add_ports(VDD3_via.get_ports_list(), prefix="VDD_TOPR_") + top_level.add_ports(VDD6_via.get_ports_list(), prefix="VDD_BOTTOML_") + top_level.add_ports(VDD8_via.get_ports_list(), prefix="VDD_BOTTOMR_") + + top_level.add_ports(VSS1_via.get_ports_list(), prefix="VSS_TOP_") + top_level.add_ports(VSS2_via.get_ports_list(), prefix="VSS_BOTTOM_") + + return component_snap_to_grid(rename_ports_by_orientation(top_level)) + +if __name__ == "__main__": + comp = cswitch(gf180) + + # comp.pprint_ports() + + comp = add_cswitch_labels(comp, gf180) + + comp.name = "CSWITCH" + + comp.write_gds('out_CSWITCH.gds') + + comp.show() + + print("...Running DRC...") + + drc_result = gf180.drc_magic(comp, "CSWITCH") + + drc_result = gf180.drc(comp) + diff --git a/src/glayout/blocks/composite/chopper_switch/README.md b/src/glayout/blocks/composite/chopper_switch/README.md new file mode 100644 index 00000000..66711699 --- /dev/null +++ b/src/glayout/blocks/composite/chopper_switch/README.md @@ -0,0 +1,74 @@ +# Chopper Switch Cell + +GenYZ Team: Oct 17 2025 + +## Schematic +Image + +This circuit is a differential chopper switch. It alternates the signal polarity between the differential inputs (IN+, IN-) and outputs (OUT+, OUT-). This implementation is constructed using pre-designed tgswitch components as the core switching elements. The opposing control signals, CLK and CLKB, determine which tgswitch path is active, selecting either a straight-through or a crossover connection. + +## Parametrizing the Chopper Switch Block +**Parameters:** +- **pdk:** Which PDK to use. +- **width:** Width per finger (µm), in a tuple for PFET and NFET respectively. +- **length:** Length per finger (µm), in a tuple for PFET and NFET respectively. +- **fingers:** Number of fingers per transistor, in a tuple for PFET and NFET respectively. +- **multipliers:** Parallel device multiplier (m-factor), in a tuple for PFET and NFET respectively. +- **dummy_1:** Enable PFET dummy gates, in a tuple for left and right dummy respectively. +- **dummy_2:** Enable NFET dummy gates, in a tuple for left and right dummy respectively. +- **tie_layers1:** PFET body-tie routing layers, in a tuple (X metal, Y metal). +- **tie_layers2:** NFET body-tie routing layers, in a tuple (X metal, Y metal). +- **sd_rmult:** Integer multiplier for source/drain contact routing width (reduces on-resistance). +- **kwargs:** Additional parameters passed directly to pdk.nmos() and pdk.pmos(). +``` +def cswitch( + pdk: MappedPDK, + width: tuple[float,float] = (10,10), + length: tuple[float,float] = (0.5,0.5), + fingers: tuple[int,int] = (6,6), + multipliers: tuple[int,int] = (1,1), + dummy_1: tuple[bool,bool] = (True,True), + dummy_2: tuple[bool,bool] = (True,True), + tie_layers1: tuple[str,str] = ("met2","met1"), + tie_layers2: tuple[str,str] = ("met2","met1"), + sd_rmult: int=1, + **kwargs + ) -> Component: +``` + +### + +### GDS generated +Image + +### DRC Report +``` +using default pdk_root +Defaulting to stale magic_commands.tcl + +Magic 8.3 revision 528 - Compiled on Wed Jun 18 09:45:25 PM CEST 2025. +Starting magic under Tcl interpreter +Using the terminal as the console. +WARNING: RLIMIT_NOFILE is above 1024 and Tcl_Version<9 this may cause runtime issues [rlim_cur=1048576] +Using NULL graphics device. +Processing system .magicrc file +Sourcing design .magicrc for technology gf180mcuD ... +10 Magic internal units = 1 Lambda +Input style import: scaleFactor=10, multiplier=2 +The following types are not handled by extraction and will be treated as non-electrical types: + obsactive mvobsactive filldiff fillpoly m1hole obsm1 fillm1 obsv1 m2hole obsm2 fillm2 obsv2 m3hole obsm3 fillm3 m4hole obsm4 fillm4 m5hole obsm5 fillm5 glass fillblock lvstext obscomment +Scaled tech values by 10 / 1 to match internal grid scaling +Loading gf180mcuD Device Generator Menu ... +Loading "/tmp/tmpsvvb6hl4/magic_commands.tcl" from command line. +Warning: Calma reading is not undoable! I hope that's OK. +Library written using GDS-II Release 6.0 +Library name: library +Reading "cswitch$3". +[INFO]: Loading cswitch$3 + +Loading DRC CIF style. +No errors found. +[INFO]: DONE with /tmp/tmpsvvb6hl4/cswitch$3.rpt + +Using technology "gf180mcuD", version 1.0.525-0-gf2e289d +``` diff --git a/src/glayout/blocks/composite/chopper_switch/__init__.py b/src/glayout/blocks/composite/chopper_switch/__init__.py new file mode 100644 index 00000000..85a5a679 --- /dev/null +++ b/src/glayout/blocks/composite/chopper_switch/__init__.py @@ -0,0 +1,10 @@ + +###Glayout CSWITCH Cell. + + +from .my_CSWITCH import cswitch,add_cswitch_labels + +__all__ = [ + 'cswitch', + 'add_cswitch_labels', +] diff --git a/src/glayout/blocks/elementary/TGSW/README.md b/src/glayout/blocks/elementary/TGSW/README.md new file mode 100644 index 00000000..cb13aa94 --- /dev/null +++ b/src/glayout/blocks/elementary/TGSW/README.md @@ -0,0 +1,74 @@ +# Transistor Gate Switch + +GenYZ Team: Oct 17 2025 + +## Schematic +![Image](https://github.com/user-attachments/assets/0cf4a515-ee1b-40af-b423-7b3b2a89e6c6) + +A transmission switch (tgswitch) contains one PMOS and one NMOS transistor. Their source terminals are connected together to form the input (IN), and their drain terminals are connected together to form the output (OUT), as seen in the schematic. + +## Parametrizing the TG Switch block +- **pdk:** Which PDK to use. +- **placement:** TG layout orientation, either "vertical" or "horizontal". +- **width:** Width per finger (µm), in a tuple for PFET and NFET respectively. +- **length:** Length per finger (µm), in a tuple for PFET and NFET respectively. +- **fingers:** Number of fingers per transistor, in a tuple for PFET and NFET respectively. +- **multipliers:** Parallel device multiplier (m-factor), in a tuple for PFET and NFET respectively. +- **dummy_1:** Enable PFET dummy gates, in a tuple for left and right dummy respectively. +- **dummy_2:** Enable NFET dummy gates, in a tuple for left and right dummy respectively. +- **tie_layers1:** PFET body-tie routing layers, in a tuple (X metal, Y metal). +- **tie_layers2:** NFET body-tie routing layers, in a tuple (X metal, Y metal). +- **sd_rmult:** Integer multiplier for source/drain contact routing width (reduces on-resistance). +- **kwargs:** Additional parameters passed directly to pdk.nmos() and pdk.pmos(). + +``` +def tgswitch( + pdk: MappedPDK, + placement: str = "vertical", + width: tuple[float,float] = (12,12), + length: tuple[float,float] = (0.5,0.5), + fingers: tuple[int,int] = (5,5), + multipliers: tuple[int,int] = (1,1), + dummy_1: tuple[bool,bool] = (True,True), + dummy_2: tuple[bool,bool] = (True,True), + tie_layers1: tuple[str,str] = ("met2","met1"), + tie_layers2: tuple[str,str] = ("met2","met1"), + sd_rmult: int=1, + **kwargs + ) -> Component: +``` + +### GDS generated +Image + +### DRC Report +``` +using default pdk_root: /usr/bin/miniconda3/share/pdk/ +Defaulting to stale magic_commands.tcl + +Magic 8.3 revision 528 - Compiled on Wed Jun 18 09:45:25 PM CEST 2025. +Starting magic under Tcl interpreter +Using the terminal as the console. +WARNING: RLIMIT_NOFILE is above 1024 and Tcl_Version<9 this may cause runtime issues [rlim_cur=1048576] +Using NULL graphics device. +Processing system .magicrc file +Sourcing design .magicrc for technology gf180mcuD ... +10 Magic internal units = 1 Lambda +Input style import: scaleFactor=10, multiplier=2 +The following types are not handled by extraction and will be treated as non-electrical types: + obsactive mvobsactive filldiff fillpoly m1hole obsm1 fillm1 obsv1 m2hole obsm2 fillm2 obsv2 m3hole obsm3 fillm3 m4hole obsm4 fillm4 m5hole obsm5 fillm5 glass fillblock lvstext obscomment +Scaled tech values by 10 / 1 to match internal grid scaling +Loading gf180mcuD Device Generator Menu ... +Loading "/tmp/tmpp2_g_ogw/magic_commands.tcl" from command line. +Warning: Calma reading is not undoable! I hope that's OK. +Library written using GDS-II Release 6.0 +Library name: library +Reading "tgswitch". +[INFO]: Loading tgswitch + +Loading DRC CIF style. +No errors found. +[INFO]: DONE with /tmp/tmpp2_g_ogw/tgswitch.rpt + +Using technology "gf180mcuD", version 1.0.525-0-gf2e289d +``` diff --git a/src/glayout/blocks/elementary/TGSW/TGSW.py b/src/glayout/blocks/elementary/TGSW/TGSW.py new file mode 100644 index 00000000..322c28ae --- /dev/null +++ b/src/glayout/blocks/elementary/TGSW/TGSW.py @@ -0,0 +1,184 @@ + +from glayout import MappedPDK, sky130 , gf180 +from gdsfactory.cell import cell +from gdsfactory import Component +from gdsfactory.components import text_freetype, rectangle + +from glayout import nmos, pmos +from glayout import via_stack +from glayout import rename_ports_by_orientation +from glayout import tapring + +from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port +from glayout.util.port_utils import add_ports_perimeter,print_ports +from glayout.util.snap_to_grid import component_snap_to_grid +from glayout.spice.netlist import Netlist + +from glayout.routing.straight_route import straight_route +from glayout.routing.c_route import c_route +from glayout.routing.L_route import L_route + +import os +import subprocess + +# Run a shell, source .bashrc, then printenv +cmd = 'bash -c "source ~/.bashrc && printenv"' +result = subprocess.run(cmd, shell=True, text=True, capture_output=True) +env_vars = {} +for line in result.stdout.splitlines(): + if '=' in line: + key, value = line.split('=', 1) + env_vars[key] = value + +# Now, update os.environ with these +os.environ.update(env_vars) + +def add_tgswitch_labels( + tgswitch_in: Component, + pdk: MappedPDK, + ) -> Component: + + tgswitch_in.unlock() + + psize=(0.5,0.5) + # list that will contain all port/comp info + move_info = list() + # create labels and append to info list + + # VSS + vsslabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + vsslabel.add_label(text="VSS",layer=pdk.get_glayer("met2_label")) + move_info.append((vsslabel,component.ports["N_tie_N_top_met_W"],None)) + #gnd_ref = top_level << gndlabel; + + #suply + vddlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + vddlabel.add_label(text="VDD",layer=pdk.get_glayer("met2_label")) + move_info.append((vddlabel,component.ports["P_tie_N_top_met_W"],None)) + #sup_ref = top_level << suplabel; + + # output + outputlabel = rectangle(layer=pdk.get_glayer("met3_pin"),size=psize,centered=True).copy() + outputlabel.add_label(text="VOUT",layer=pdk.get_glayer("met3_label")) + move_info.append((outputlabel,component.ports["P_drain_top_met_N"],None)) + #op_ref = top_level << outputlabel; + + # input + inputlabel = rectangle(layer=pdk.get_glayer("met3_pin"),size=psize,centered=True).copy() + inputlabel.add_label(text="VIN",layer=pdk.get_glayer("met3_label")) + move_info.append((inputlabel,component.ports["P_source_top_met_N"], None)) + #ip_ref = top_level << inputlabel; + + # CLK + clklabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + clklabel.add_label(text="CLK",layer=pdk.get_glayer("met2_label")) + move_info.append((clklabel,component.ports["N_gate_S"], None)) + + # CLK_INV + clkinvlabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=psize,centered=True).copy() + clkinvlabel.add_label(text="CLKinv",layer=pdk.get_glayer("met2_label")) + move_info.append((clkinvlabel,component.ports["P_gate_N"], None)) + + for comp, prt, alignment in move_info: + alignment = ('c','b') if alignment is None else alignment + compref = align_comp_to_port(comp, prt, alignment=alignment) + tgswitch_in.add(compref) + + return tgswitch_in.flatten() +@cell +def tgswitch( + pdk: MappedPDK, + placement: str = "vertical", + width: tuple[float,float] = (12,12), + length: tuple[float,float] = (0.5,0.5), + fingers: tuple[int,int] = (5,5), + multipliers: tuple[int,int] = (1,1), + dummy_1: tuple[bool,bool] = (True,True), + dummy_2: tuple[bool,bool] = (True,True), + tie_layers1: tuple[str,str] = ("met2","met1"), + tie_layers2: tuple[str,str] = ("met2","met1"), + sd_rmult: int=1, + **kwargs + ) -> Component: + + pdk.activate() + + #top level component + top_level = Component(name="tgswitch") + + #two fets + fet_P = pmos(pdk, width=width[0], fingers=fingers[0], multipliers=multipliers[0], with_dummy=dummy_1, with_substrate_tap=False, length=length[0], tie_layers=tie_layers1, sd_rmult=sd_rmult, **kwargs ) + fet_N = nmos(pdk, width=width[1], fingers=fingers[1], multipliers=multipliers[1], with_dummy=dummy_2, with_substrate_tap=False, length=length[1], tie_layers=tie_layers2, sd_rmult=sd_rmult, with_dnwell=False, **kwargs) + + fet_P_ref = top_level << fet_P + fet_N_ref = top_level << fet_N + fet_P_ref.name = "fet_P" + fet_N_ref.name = "fet_N" + + #Relative move + ref_dimensions = evaluate_bbox(fet_N) + if placement == "vertical": + # display_component(top_level, scale = 2,path="../../") + fet_N_ref.mirror_y() + fet_N_ref.movey(fet_P_ref.ymin - ref_dimensions[1]/2 - pdk.util_max_metal_seperation()-0.5) + elif placement == "vertical_inv": + # display_component(top_level, scale = 2,path="../../") + fet_P_ref.mirror_y() + fet_P_ref.movey(fet_N_ref.ymin - ref_dimensions[1]/2 - pdk.util_max_metal_seperation()-0.5) + elif placement == "horizontal": + # display_component(top_level, scale = 2,path="../../") + fet_N_ref.movex(fet_P_ref.xmax + ref_dimensions[0]/2 + pdk.util_max_metal_seperation()+1) + else: + raise ValueError("Placement must be either 'vertical', 'vertical _inv', or 'horizontal'") + + #Routing + viam2m3 = via_stack(pdk, "met2", "met3", centered=True) #met2 is the bottom layer. met3 is the top layer. + + #via for input and output + drain_P_via = top_level << viam2m3 + source_P_via = top_level << viam2m3 + + if placement == "vertical": + drain_P_via.move(fet_P_ref.ports["multiplier_0_source_W"].center).movex(-0.75).movey((fet_N_ref.ymin-fet_N_ref.ymax)/1.7) + source_P_via.move(fet_P_ref.ports["multiplier_0_drain_E"].center).movex(0.75).movey((fet_N_ref.ymin-fet_N_ref.ymax)/1.7) + top_level << c_route(pdk, fet_P_ref.ports["multiplier_0_source_W"], fet_N_ref.ports["multiplier_0_source_W"]) + top_level << c_route(pdk, fet_P_ref.ports["multiplier_0_drain_E"], fet_N_ref.ports["multiplier_0_drain_E"]) + elif placement == "vertical_inv": + drain_P_via.move(fet_N_ref.ports["multiplier_0_source_W"].center).movex(-0.75).movey((fet_P_ref.ymin-fet_P_ref.ymax)/1.7) + source_P_via.move(fet_N_ref.ports["multiplier_0_drain_E"].center).movex(0.75).movey((fet_P_ref.ymin-fet_P_ref.ymax)/1.7) + top_level << c_route(pdk, fet_P_ref.ports["multiplier_0_source_W"], fet_N_ref.ports["multiplier_0_source_W"]) + top_level << c_route(pdk, fet_P_ref.ports["multiplier_0_drain_E"], fet_N_ref.ports["multiplier_0_drain_E"]) + elif placement == "horizontal": + source_P_via.move(fet_P_ref.ports["multiplier_0_source_W"].center).movex(ref_dimensions[0]*0.9) + drain_P_via.move(fet_P_ref.ports["multiplier_0_drain_E"].center).movex(ref_dimensions[0]*0.15) + top_level << straight_route(pdk, fet_P_ref.ports["multiplier_0_source_E"], fet_N_ref.ports["multiplier_0_source_W"]) + top_level << straight_route(pdk, fet_P_ref.ports["multiplier_0_drain_E"], fet_N_ref.ports["multiplier_0_drain_W"]) + else: + raise ValueError("Placement must be either 'vertical', 'vertical_inv', or 'horizontal'.") + + top_level.add_ports(fet_P_ref.get_ports_list(), prefix="P_") + top_level.add_ports(fet_N_ref.get_ports_list(), prefix="N_") + top_level.add_ports(drain_P_via.get_ports_list(), prefix="P_drain_") + top_level.add_ports(source_P_via.get_ports_list(), prefix="P_source_") + + return component_snap_to_grid(rename_ports_by_orientation(top_level)) + +if __name__ == "__main__": + comp = tgswitch(gf180) + + # comp.pprint_ports() + + comp = add_tgswitch_labels(comp, gf180) + + comp.name = "TGSW" + + comp.write_gds('out_TGSW.gds') + + comp.show() + + print("...Running DRC...") + + drc_result = gf180.drc_magic(comp, "TGSW") + + drc_result = gf180.drc(comp) + diff --git a/src/glayout/blocks/elementary/TGSW/__init__.py b/src/glayout/blocks/elementary/TGSW/__init__.py new file mode 100644 index 00000000..444a6608 --- /dev/null +++ b/src/glayout/blocks/elementary/TGSW/__init__.py @@ -0,0 +1,10 @@ + +###Glayout tgswitch Cell. + + +from .my_TGSW import tgswitch,add_tgswitch_labels + +__all__ = [ + 'tgswitch', + 'add_tgswitch_labels', +]