Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added draft PR of generator for strongARM comparator #338

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from gdsfactory.components import rectangle
from gdsfactory import Component
from glayout.flow.pdk.mappedpdk import MappedPDK
from glayout.flow.primitives.fet import nmos, pmos
from glayout.flow.placement.two_transistor_interdigitized import two_pfet_interdigitized
from four_transistor_interdigitized import generic_4T_interdigitzed
from glayout.flow.pdk.util.comp_utils import prec_ref_center
from glayout.flow.routing.smart_route import smart_route, c_route, straight_route, L_route
from glayout.flow.pdk.sky130_mapped import sky130_mapped_pdk
from glayout.flow.spice import Netlist
from glayout.flow.primitives.guardring import tapring
from glayout.flow.primitives.via_gen import via_stack, via_array

def x_coupled_netlist(nfetA: Component, nfetB: Component, pfetA: Component, pfetB: Component, nfetdum: Component, pfetdum: Component):
x_coupled_netlist = Netlist(circuit_name ='cross_cpoupled_load', nodes=['VSN1', 'VSN2', 'VSP1', 'VSP2', 'VO1', 'VO2', 'VBULKN', 'VBULKP'])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal but there is a typo in the circuit name

x_coupled_netlist.connect_netlist(
nfetA.info['netlist'],
[('G','VO1'), ('D','VO2'), ('S','VSN1'), ('B','VBULKN')]
)
x_coupled_netlist.connect_netlist(
nfetdum.info['netlist'],
[('G','VBULKN'), ('D','VBULKN'), ('S','VBULKN'), ('B','VBULKN')]
)
x_coupled_netlist.connect_netlist(
pfetdum.info['netlist'],
[('G','VBULKP'), ('D','VBULKP'), ('S','VBULKP'), ('B','VBULKP')]
)
x_coupled_netlist.connect_netlist(
nfetB.info['netlist'],
[('G','VO2'), ('D','VO1'), ('S','VSN2'), ('B','VBULKN')]
)
x_coupled_netlist.connect_netlist(
pfetA.info['netlist'],
[('G','VO1'), ('D','VO2'), ('S','VSP1'), ('B','VBULKP')]
)
x_coupled_netlist.connect_netlist(
pfetB.info['netlist'],
[('G','VO2'), ('D','VO1'), ('S','VSP2'), ('B','VBULKP')]
)
return x_coupled_netlist

def cross_coupled_load(pdk: MappedPDK, ccinv_col):
cross_coupled_load=Component(name="cross_coupled_load")
cross_couple = generic_4T_interdigitzed(pdk, numcols=ccinv_col, top_row_device="pfet", bottom_row_device="nfet", length=0.4, with_substrate_tap = False)
cross_couple_ref = prec_ref_center(cross_couple)
cross_coupled_load.add(cross_couple_ref)
cross_coupled_load.add_ports(cross_couple_ref.get_ports_list(), prefix="cross_couple_")
cross_coupled_load << smart_route(pdk,cross_coupled_load.ports["cross_couple_top_A_source_E"],cross_coupled_load.ports["cross_couple_top_B_source_E"],cross_couple_ref,cross_coupled_load)
cross_coupled_load << c_route(pdk,cross_coupled_load.ports["cross_couple_top_A_drain_E"],cross_coupled_load.ports["cross_couple_top_B_gate_E"])
cross_coupled_load << c_route(pdk,cross_coupled_load.ports["cross_couple_bottom_A_drain_E"],cross_coupled_load.ports["cross_couple_bottom_B_gate_E"])
cross_coupled_load << c_route(pdk,cross_coupled_load.ports["cross_couple_top_B_drain_W"],cross_coupled_load.ports["cross_couple_top_A_gate_W"])
cross_coupled_load << c_route(pdk,cross_coupled_load.ports["cross_couple_bottom_B_drain_W"],cross_coupled_load.ports["cross_couple_bottom_A_gate_W"])
cross_coupled_load << c_route(pdk,cross_coupled_load.ports["cross_couple_top_B_gate_E"],cross_coupled_load.ports["cross_couple_bottom_A_drain_E"])
cross_coupled_load << c_route(pdk,cross_coupled_load.ports["cross_couple_top_A_gate_W"],cross_coupled_load.ports["cross_couple_bottom_B_drain_W"])

nfetA = nmos(pdk, width=3*ccinv_col, length=0.4, with_dummy=(False, False))
nfetB = nmos(pdk, width=3*ccinv_col, length=0.4, with_dummy=(False, False))
pfetA = pmos(pdk, width=3*ccinv_col, length=0.4, with_dummy=(False, False))
pfetB = pmos(pdk, width=3*ccinv_col, length=0.4, with_dummy=(False, False))
nfetdum = nmos(pdk, width=3*2, length=0.4, with_dummy=(False, False))
pfetdum = pmos(pdk, width=3*2, length=0.4, with_dummy=(False, False))
cross_coupled_load.info['netlist'] = x_coupled_netlist(nfetA, nfetB, pfetA, pfetB, nfetdum, pfetdum)
return cross_coupled_load


def add_x_coupled_labels(x_coupled: Component):
x_coupled.unlock()
met1_label = (68, 5)
met1_pin = (68, 16)
move_info = list()
#vo1 label
vo1label = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vo1label.add_label(text="VO1",layer=met1_label)
move_info.append((vo1label,x_coupled.ports["cross_couple_top_B_drain_N"],None))
#vo2 label
vo2label = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vo2label.add_label(text="VO2",layer=met1_label)
move_info.append((vo2label,x_coupled.ports["cross_couple_top_A_drain_N"],None))
#vsn1 label
vsn1label = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vsn1label.add_label(text="VSN1",layer=met1_label)
move_info.append((vsn1label,x_coupled.ports["cross_couple_bottom_A_source_N"],None))
#vsn2 label
vsn2label = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vsn2label.add_label(text="VSN2",layer=met1_label)
move_info.append((vsn2label,x_coupled.ports["cross_couple_bottom_B_source_N"],None))
#vsp1 label
vsp1label = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vsp1label.add_label(text="VSP1",layer=met1_label)
move_info.append((vsp1label,x_coupled.ports["cross_couple_top_A_source_N"],None))
#vsp2 label
vsp2label = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vsp2label.add_label(text="VSP2",layer=met1_label)
move_info.append((vsp2label,x_coupled.ports["cross_couple_top_B_source_N"],None))
#vbulk label
vbulknlabel = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vbulknlabel.add_label(text="VBULKN",layer=met1_label)
move_info.append((vbulknlabel,x_coupled.ports["cross_couple_bottom_welltie_N_top_met_N"],None))
#vbulk label
vbulkplabel = rectangle(layer=met1_pin, size=(0.05,0.05), centered=True).copy()
vbulkplabel.add_label(text="VBULKP",layer=met1_label)
move_info.append((vbulkplabel,x_coupled.ports["cross_couple_top_welltie_N_top_met_N"],None))
#move everything to position
for comp, prt, alignment in move_info:
alignment = ('c','b') if alignment is None else alignment
compref = align_comp_to_port(comp, prt, alignment=alignment)
x_coupled.add(compref)
return x_coupled.flatten()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a copy of the existing diff pair or are there differences? If it is a copy, you can directly import and use it. If not, can the changes be incorporated into the main one as options/parameters?

Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
from typing import Optional, Union

from gdsfactory.cell import cell
from gdsfactory.component import Component, copy
from gdsfactory.components.rectangle import rectangle
from gdsfactory.routing.route_quad import route_quad
from gdsfactory.routing.route_sharp import route_sharp
from glayout.flow.pdk.mappedpdk import MappedPDK
from glayout.flow.pdk.util.comp_utils import align_comp_to_port, evaluate_bbox, movex, movey
from glayout.flow.pdk.util.port_utils import (
add_ports_perimeter,
get_orientation,
print_ports,
rename_ports_by_list,
rename_ports_by_orientation,
set_port_orientation,
)
from glayout.flow.pdk.util.snap_to_grid import component_snap_to_grid
from glayout.flow.placement.common_centroid_ab_ba import common_centroid_ab_ba
from glayout.flow.primitives.fet import nmos, pmos
from glayout.flow.primitives.guardring import tapring
from glayout.flow.primitives.via_gen import via_stack
from glayout.flow.routing.c_route import c_route
from glayout.flow.routing.smart_route import smart_route
from glayout.flow.routing.straight_route import straight_route
from glayout.flow.spice import Netlist


def diff_pair_netlist(fetL: Component, fetR: Component) -> Netlist:
diff_pair_netlist = Netlist(circuit_name='DIFF_PAIR', nodes=['VP', 'VN', 'VDD1', 'VDD2', 'VTAIL', 'B'])
diff_pair_netlist.connect_netlist(
fetL.info['netlist'],
[('D', 'VDD1'), ('G', 'VP'), ('S', 'VTAIL'), ('B', 'B')]
)
diff_pair_netlist.connect_netlist(
fetR.info['netlist'],
[('D', 'VDD2'), ('G', 'VN'), ('S', 'VTAIL'), ('B', 'B')]
)
return diff_pair_netlist

@cell
def diff_pair(
pdk: MappedPDK,
width: float = 3,
fingers: int = 4,
length: Optional[float] = None,
n_or_p_fet: bool = True,
plus_minus_seperation: float = 0,
rmult: int = 1,
dummy: Union[bool, tuple[bool, bool]] = True,
substrate_tap: bool=True
) -> Component:
"""create a diffpair with 2 transistors placed in two rows with common centroid place. Sources are shorted
width = width of the transistors
fingers = number of fingers in the transistors (must be 2 or more)
length = length of the transistors, None or 0 means use min length
short_source = if true connects source of both transistors
n_or_p_fet = if true the diffpair is made of nfets else it is made of pfets
substrate_tap: if true place a tapring around the diffpair (connects on met1)
"""
# TODO: error checking
pdk.activate()
diffpair = Component()
# create transistors
well = None
if isinstance(dummy, bool):
dummy = (dummy, dummy)
if n_or_p_fet:
fetL = nmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(dummy[0], False),with_dnwell=False,with_substrate_tap=False,rmult=rmult)
fetR = nmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(False,dummy[1]),with_dnwell=False,with_substrate_tap=False,rmult=rmult)
fetL2 = nmos(pdk, width=width*2, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(dummy[0], False),with_dnwell=False,with_substrate_tap=False,rmult=rmult)
fetR2 = nmos(pdk, width=width*2, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(False,dummy[1]),with_dnwell=False,with_substrate_tap=False,rmult=rmult)

min_spacing_x = pdk.get_grule("n+s/d")["min_separation"] - 2*(fetL.xmax - fetL.ports["multiplier_0_plusdoped_E"].center[0])
well = "pwell"
else:
fetL = pmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(dummy[0], False),dnwell=False,with_substrate_tap=False,rmult=rmult)
fetR = pmos(pdk, width=width, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(False,dummy[1]),dnwell=False,with_substrate_tap=False,rmult=rmult)
fetL2 = pmos(pdk, width=width*2, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(dummy[0], False),with_dnwell=False,with_substrate_tap=False,rmult=rmult)
fetR2 = pmos(pdk, width=width*2, fingers=fingers,length=length,multipliers=1,with_tie=False,with_dummy=(False,dummy[1]),with_dnwell=False,with_substrate_tap=False,rmult=rmult)

min_spacing_x = pdk.get_grule("p+s/d")["min_separation"] - 2*(fetL.xmax - fetL.ports["multiplier_0_plusdoped_E"].center[0])
well = "nwell"
# place transistors
viam2m3 = via_stack(pdk,"met2","met3",centered=True)
metal_min_dim = max(pdk.get_grule("met2")["min_width"],pdk.get_grule("met3")["min_width"])
metal_space = max(pdk.get_grule("met2")["min_separation"],pdk.get_grule("met3")["min_separation"],metal_min_dim)
gate_route_os = evaluate_bbox(viam2m3)[0] - fetL.ports["multiplier_0_gate_W"].width + metal_space
min_spacing_y = metal_space + 2*gate_route_os
min_spacing_y = min_spacing_y - 2*abs(fetL.ports["well_S"].center[1] - fetL.ports["multiplier_0_gate_S"].center[1])
# TODO: fix spacing where you see +-0.5
a_topl = (diffpair << fetL).movey(fetL.ymax+min_spacing_y/2+0.5).movex(0-fetL.xmax-min_spacing_x/2)
b_topr = (diffpair << fetR).movey(fetR.ymax+min_spacing_y/2+0.5).movex(fetL.xmax+min_spacing_x/2)
a_botr = (diffpair << fetR)
a_botr.mirror_y().movey(0-0.5-fetL.ymax-min_spacing_y/2).movex(fetL.xmax+min_spacing_x/2)
b_botl = (diffpair << fetL)
b_botl.mirror_y().movey(0-0.5-fetR.ymax-min_spacing_y/2).movex(0-fetL.xmax-min_spacing_x/2)
# if substrate tap place substrate tap
if substrate_tap:
tapref = diffpair << tapring(pdk,evaluate_bbox(diffpair,padding=1),horizontal_glayer="met1")
diffpair.add_ports(tapref.get_ports_list(),prefix="tap_")
try:
diffpair<<straight_route(pdk,a_topl.ports["multiplier_0_dummy_L_gsdcon_top_met_W"],diffpair.ports["tap_W_top_met_W"],glayer2="met1")
except KeyError:
pass
try:
diffpair<<straight_route(pdk,b_topr.ports["multiplier_0_dummy_R_gsdcon_top_met_W"],diffpair.ports["tap_E_top_met_E"],glayer2="met1")
except KeyError:
pass
try:
diffpair<<straight_route(pdk,b_botl.ports["multiplier_0_dummy_L_gsdcon_top_met_W"],diffpair.ports["tap_W_top_met_W"],glayer2="met1")
except KeyError:
pass
try:
diffpair<<straight_route(pdk,a_botr.ports["multiplier_0_dummy_R_gsdcon_top_met_W"],diffpair.ports["tap_E_top_met_E"],glayer2="met1")
except KeyError:
pass
# route sources (short sources)
diffpair << route_quad(a_topl.ports["multiplier_0_source_E"], b_topr.ports["multiplier_0_source_W"], layer=pdk.get_glayer("met2"))
diffpair << route_quad(b_botl.ports["multiplier_0_source_E"], a_botr.ports["multiplier_0_source_W"], layer=pdk.get_glayer("met2"))
sextension = b_topr.ports["well_E"].center[0] - b_topr.ports["multiplier_0_source_E"].center[0]
source_routeE = diffpair << c_route(pdk, b_topr.ports["multiplier_0_source_E"], a_botr.ports["multiplier_0_source_E"],extension=sextension)
source_routeW = diffpair << c_route(pdk, a_topl.ports["multiplier_0_source_W"], b_botl.ports["multiplier_0_source_W"],extension=sextension)
# route drains
# place via at the drain
drain_br_via = diffpair << viam2m3
drain_bl_via = diffpair << viam2m3
drain_br_via.move(a_botr.ports["multiplier_0_drain_N"].center).movey(viam2m3.ymin)
drain_bl_via.move(b_botl.ports["multiplier_0_drain_N"].center).movey(viam2m3.ymin)
drain_br_viatm = diffpair << viam2m3
drain_bl_viatm = diffpair << viam2m3
drain_br_viatm.move(a_botr.ports["multiplier_0_drain_N"].center).movey(viam2m3.ymin)
drain_bl_viatm.move(b_botl.ports["multiplier_0_drain_N"].center).movey(-1.5 * evaluate_bbox(viam2m3)[1] - metal_space)
# create route to drain via
width_drain_route = b_topr.ports["multiplier_0_drain_E"].width
dextension = source_routeE.xmax - b_topr.ports["multiplier_0_drain_E"].center[0] + metal_space
bottom_extension = viam2m3.ymax + width_drain_route/2 + 2*metal_space
drain_br_viatm.movey(0-bottom_extension - metal_space - width_drain_route/2 - viam2m3.ymax)
diffpair << route_quad(drain_br_viatm.ports["top_met_N"], drain_br_via.ports["top_met_S"], layer=pdk.get_glayer("met3"))
diffpair << route_quad(drain_bl_viatm.ports["top_met_N"], drain_bl_via.ports["top_met_S"], layer=pdk.get_glayer("met3"))
floating_port_drain_bottom_L = set_port_orientation(movey(drain_bl_via.ports["bottom_met_W"],0-bottom_extension), get_orientation("E"))
floating_port_drain_bottom_R = set_port_orientation(movey(drain_br_via.ports["bottom_met_E"],0-bottom_extension - metal_space - width_drain_route), get_orientation("W"))
drain_routeTR_BL = diffpair << c_route(pdk, floating_port_drain_bottom_L, b_topr.ports["multiplier_0_drain_E"],extension=dextension, width1=width_drain_route,width2=width_drain_route)
drain_routeTL_BR = diffpair << c_route(pdk, floating_port_drain_bottom_R, a_topl.ports["multiplier_0_drain_W"],extension=dextension, width1=width_drain_route,width2=width_drain_route)
# cross gate route top with c_route. bar_minus ABOVE bar_plus
get_left_extension = lambda bar, a_topl=a_topl, diffpair=diffpair, pdk=pdk : (abs(diffpair.xmin-min(a_topl.ports["multiplier_0_gate_W"].center[0],bar.ports["e1"].center[0])) + pdk.get_grule("met2")["min_separation"])
get_right_extension = lambda bar, b_topr=b_topr, diffpair=diffpair, pdk=pdk : (abs(diffpair.xmax-max(b_topr.ports["multiplier_0_gate_E"].center[0],bar.ports["e3"].center[0])) + pdk.get_grule("met2")["min_separation"])
# lay bar plus and PLUSgate_routeW
bar_comp = rectangle(centered=True,size=(abs(b_topr.xmax-a_topl.xmin), b_topr.ports["multiplier_0_gate_E"].width),layer=pdk.get_glayer("met2"))
bar_plus = (diffpair << bar_comp).movey(diffpair.ymax + bar_comp.ymax + pdk.get_grule("met2")["min_separation"])
PLUSgate_routeW = diffpair << c_route(pdk, a_topl.ports["multiplier_0_gate_W"], bar_plus.ports["e1"], extension=get_left_extension(bar_plus))
# lay bar minus and MINUSgate_routeE
plus_minus_seperation = max(pdk.get_grule("met2")["min_separation"], plus_minus_seperation)
bar_minus = (diffpair << bar_comp).movey(diffpair.ymax +bar_comp.ymax + plus_minus_seperation)
MINUSgate_routeE = diffpair << c_route(pdk, b_topr.ports["multiplier_0_gate_E"], bar_minus.ports["e3"], extension=get_right_extension(bar_minus))
# lay MINUSgate_routeW and PLUSgate_routeE
MINUSgate_routeW = diffpair << c_route(pdk, set_port_orientation(b_botl.ports["multiplier_0_gate_E"],"W"), bar_minus.ports["e1"], extension=get_left_extension(bar_minus))
PLUSgate_routeE = diffpair << c_route(pdk, set_port_orientation(a_botr.ports["multiplier_0_gate_W"],"E"), bar_plus.ports["e3"], extension=get_right_extension(bar_plus))
# correct pwell place, add ports, flatten, and return
diffpair.add_ports(a_topl.get_ports_list(),prefix="tl_")
diffpair.add_ports(b_topr.get_ports_list(),prefix="tr_")
diffpair.add_ports(b_botl.get_ports_list(),prefix="bl_")
diffpair.add_ports(a_botr.get_ports_list(),prefix="br_")
diffpair.add_ports(source_routeE.get_ports_list(),prefix="source_routeE_")
diffpair.add_ports(source_routeW.get_ports_list(),prefix="source_routeW_")
diffpair.add_ports(drain_routeTR_BL.get_ports_list(),prefix="drain_routeTR_BL_")
diffpair.add_ports(drain_routeTL_BR.get_ports_list(),prefix="drain_routeTL_BR_")
diffpair.add_ports(MINUSgate_routeW.get_ports_list(),prefix="MINUSgateroute_W_")
diffpair.add_ports(MINUSgate_routeE.get_ports_list(),prefix="MINUSgateroute_E_")
diffpair.add_ports(PLUSgate_routeW.get_ports_list(),prefix="PLUSgateroute_W_")
diffpair.add_ports(PLUSgate_routeE.get_ports_list(),prefix="PLUSgateroute_E_")
diffpair.add_padding(layers=(pdk.get_glayer(well),), default=0)

component = component_snap_to_grid(rename_ports_by_orientation(diffpair))

component.info['netlist'] = diff_pair_netlist(fetL2, fetR2)
return component



@cell
def diff_pair_generic(
pdk: MappedPDK,
width: float = 3,
fingers: int = 4,
length: Optional[float] = None,
n_or_p_fet: bool = True,
plus_minus_seperation: float = 0,
rmult: int = 1,
dummy: Union[bool, tuple[bool, bool]] = True,
substrate_tap: bool=True
) -> Component:
diffpair = common_centroid_ab_ba(pdk,width,fingers,length,n_or_p_fet,rmult,dummy,substrate_tap)
diffpair << smart_route(pdk,diffpair.ports["A_source_E"],diffpair.ports["B_source_E"],diffpair, diffpair)
return diffpair
Loading