Skip to content
Open
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
191 changes: 49 additions & 142 deletions src/glayout/blocks/elementary/current_mirror/current_mirror.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from glayout import MappedPDK, sky130,gf180
from glayout.routing import c_route,L_route,straight_route
from glayout.spice.netlist import Netlist
from glayout.placement.two_transistor_interdigitized import two_nfet_interdigitized, two_pfet_interdigitized
from glayout.pdk.mappedpdk import MappedPDK
from glayout.routing.c_route import c_route
from glayout.routing.L_route import L_route
from glayout.routing.straight_route import straight_route
from glayout.spice.netlist import Netlist
from glayout.pdk.sky130_mapped import sky130_mapped_pdk as sky130
from glayout.primitives.fet import nmos, pmos
from glayout.primitives.guardring import tapring
from glayout.util.port_utils import add_ports_perimeter,rename_ports_by_orientation
from gdsfactory.component import Component
from gdsfactory.cell import cell
from glayout.util.comp_utils import evaluate_bbox, prec_center, prec_ref_center, align_comp_to_port
from typing import Optional, Union
from typing import Optional, Union
from glayout.pdk.sky130_mapped import sky130_mapped_pdk
from glayout.primitives.via_gen import via_stack
from gdsfactory.components import text_freetype, rectangle

Expand All @@ -19,36 +22,35 @@
print("Warning: evaluator_wrapper not found. Evaluation will be skipped.")
run_evaluation = None

def sky130_add_cm_labels(cm_in: Component) -> Component:
def add_cm_labels(cm_in: Component,
pdk: MappedPDK
) -> Component:

cm_in.unlock()

# define layers`
met1_pin = (68,16)
met1_label = (68,5)
met2_pin = (69,16)
met2_label = (69,5)
met2_pin = (68,16)
met2_label = (68,5)

# list that will contain all port/comp info
move_info = list()
# create labels and append to info list
# vss
vsslabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy()
vsslabel.add_label(text="VSS",layer=met1_label)
vsslabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy()
vsslabel.add_label(text="VSS",layer=pdk.get_glayer("met2_label"))
move_info.append((vsslabel,cm_in.ports["fet_A_source_E"],None))

# vref
vreflabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy()
vreflabel.add_label(text="VREF",layer=met1_label)
vreflabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy()
vreflabel.add_label(text="VREF",layer=pdk.get_glayer("met2_label"))
move_info.append((vreflabel,cm_in.ports["fet_A_drain_N"],None))

# vcopy
vcopylabel = rectangle(layer=met1_pin,size=(0.27,0.27),centered=True).copy()
vcopylabel.add_label(text="VCOPY",layer=met1_label)
vcopylabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.27,0.27),centered=True).copy()
vcopylabel.add_label(text="VCOPY",layer=pdk.get_glayer("met2_label"))
move_info.append((vcopylabel,cm_in.ports["fet_B_drain_N"],None))

# VB
vblabel = rectangle(layer=met1_pin,size=(0.5,0.5),centered=True).copy()
vblabel.add_label(text="VB",layer=met1_label)
vblabel = rectangle(layer=pdk.get_glayer("met2_pin"),size=(0.5,0.5),centered=True).copy()
vblabel.add_label(text="VB",layer=pdk.get_glayer("met2_label"))
move_info.append((vblabel,cm_in.ports["welltie_S_top_met_S"], None))

# move everything to position
Expand All @@ -63,7 +65,7 @@ def current_mirror_netlist(
width: float,
length: float,
multipliers: int,
with_dummy: Optional[bool] = False,
with_dummy: bool = True,
n_or_p_fet: Optional[str] = 'nfet',
subckt_only: Optional[bool] = False
) -> Netlist:
Expand Down Expand Up @@ -97,7 +99,7 @@ def current_mirror_netlist(
)


@cell
#@cell
def current_mirror(
pdk: MappedPDK,
numcols: int = 3,
Expand Down Expand Up @@ -158,10 +160,10 @@ def current_mirror(
if device in ['pmos','pfet']:
tap_layer = "n+s/d"
tap_sep = max(
pdk.util_max_metal_seperation(),
pdk.get_grule("active_diff", "active_tap")["min_separation"],
float(pdk.util_max_metal_seperation()),
float(pdk.get_grule("active_diff", "active_tap")["min_separation"]),
)
tap_sep += pdk.get_grule(tap_layer, "active_tap")["min_enclosure"]
tap_sep += float(pdk.get_grule(tap_layer, "active_tap")["min_enclosure"])
tap_encloses = (
2 * (tap_sep + interdigitized_fets.xmax),
2 * (tap_sep + interdigitized_fets.ymax),
Expand Down Expand Up @@ -202,129 +204,34 @@ def current_mirror(
top_level.add_ports(source_short.get_ports_list(), prefix='purposegndports')


top_level.info['netlist'] = current_mirror_netlist(
current_mirror_obj= current_mirror_netlist(
pdk,
width=kwargs.get('width', 3), length=kwargs.get('length', 0.15), multipliers=numcols, with_dummy=with_dummy,
width=kwargs.get('width', 3),
length=kwargs.get('length', 0.15),
multipliers=numcols,
with_dummy=with_dummy,
n_or_p_fet=device,
subckt_only=True
)

return top_level

top_level.info['netlist'] = current_mirror_obj.generate_netlist()

def sky130_add_current_mirror_labels(current_mirror_in: Component) -> Component:
"""
Add labels to current mirror component for simulation and testing
"""
current_mirror_in.unlock()
# define layers
met1_pin = (68,16)
met1_label = (68,5)
met2_pin = (69,16)
met2_label = (69,5)
# list that will contain all port/comp info
move_info = list()

# Reference voltage (drain of reference transistor)
vref_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy()
vref_label.add_label(text="VREF", layer=met1_label)

# Copy current output (drain of mirror transistor)
vcopy_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy()
vcopy_label.add_label(text="VCOPY", layer=met1_label)

# Ground/VSS (source connections)
vss_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy()
vss_label.add_label(text="VSS", layer=met1_label)

# Bulk/VB (bulk/body connections)
vb_label = rectangle(layer=met1_pin, size=(0.5,0.5), centered=True).copy()
vb_label.add_label(text="VB", layer=met1_label)

# Try to find appropriate ports and add labels
try:
# Look for drain ports for VREF and VCOPY
ref_drain_ports = [p for p in current_mirror_in.ports.keys() if 'A_drain' in p and 'met' in p]
copy_drain_ports = [p for p in current_mirror_in.ports.keys() if 'B_drain' in p and 'met' in p]
source_ports = [p for p in current_mirror_in.ports.keys() if 'source' in p and 'met' in p]
bulk_ports = [p for p in current_mirror_in.ports.keys() if ('tie' in p or 'well' in p) and 'met' in p]

if ref_drain_ports:
move_info.append((vref_label, current_mirror_in.ports[ref_drain_ports[0]], None))
if copy_drain_ports:
move_info.append((vcopy_label, current_mirror_in.ports[copy_drain_ports[0]], None))
if source_ports:
move_info.append((vss_label, current_mirror_in.ports[source_ports[0]], None))
if bulk_ports:
move_info.append((vb_label, current_mirror_in.ports[bulk_ports[0]], None))

except (KeyError, IndexError):
# Fallback - just add labels at component center
print("Warning: Could not find specific ports for labels, using fallback positioning")
move_info = [
(vref_label, None, None),
(vcopy_label, None, None),
(vss_label, None, None),
(vb_label, None, None)
]

# move everything to position
for comp, prt, alignment in move_info:
alignment = ('c','b') if alignment is None else alignment
if prt is not None:
compref = align_comp_to_port(comp, prt, alignment=alignment)
else:
compref = comp
current_mirror_in.add(compref)

return current_mirror_in.flatten()

import json
top_level.info['netlist_dict'] = json.dumps({
'circuit_name': current_mirror_obj.circuit_name,
'nodes': current_mirror_obj.nodes,
'source_netlist': current_mirror_obj.source_netlist,
'instance_format': current_mirror_obj.instance_format,
'parameters':current_mirror_obj.parameters if hasattr(current_mirror_obj, 'parameters') else {}
})

# Create and evaluate a current mirror instance
if __name__ == "__main__":
# OLD EVAL CODE
# comp = current_mirror(sky130)
# # comp.pprint_ports()
# comp = add_cm_labels(comp,sky130)
# comp.name = "CM"
# comp.show()
# #print(comp.info['netlist'].generate_netlist())
# print("...Running DRC...")
# drc_result = sky130.drc_magic(comp, "CM")
# ## Klayout DRC
# #drc_result = sky130.drc(comp)\n

# time.sleep(5)

# print("...Running LVS...")
# lvs_res=sky130.lvs_netgen(comp, "CM")
# #print("...Saving GDS...")
# #comp.write_gds('out_CMirror.gds')

# NEW EVAL CODE
# Create current mirror with labels
cm = sky130_add_current_mirror_labels(
current_mirror(
pdk=sky130,
numcols=3,
device='nfet',
width=3,
length=1,
with_dummy=True,
with_tie=True
)
)

# Show the layout
cm.show()
cm.name = "current_mirror"

# Write GDS file
cm_gds = cm.write_gds("current_mirror.gds")
return top_level

# Run evaluation if available
if run_evaluation is not None:
result = run_evaluation("current_mirror.gds", cm.name, cm)
print(result)
else:
print("Evaluation skipped - evaluator_wrapper not available")
if __name__=="__main__":
current_mirror = add_cm_labels(current_mirror(sky130_mapped_pdk, device='pfet'),sky130_mapped_pdk)
#current_mirror.show()
current_mirror.name = "CMIRROR"
#magic_drc_result = sky130_mapped_pdk.drc_magic(current_mirror, current_mirror.name)
#netgen_lvs_result = sky130_mapped_pdk.lvs_netgen(current_mirror, current_mirror.name)
current_mirror_gds = current_mirror.write_gds("current_mirror.gds")
res = run_evaluation("current_mirror.gds", current_mirror.name, current_mirror)
Loading