diff --git a/README.md b/README.md
index e8d5e4cc..b3ad96f9 100644
--- a/README.md
+++ b/README.md
@@ -22,12 +22,12 @@ This package contains the Skywater 130nm PDK for KLayout.
You have two options for using this package:
1. Clone this repository
-2. Install the complete sky130 PDK from [open_pdks](https://github.com/RTimothyEdwards/open_pdks) either manually or with [volare](https://github.com/efabless/volare). The PDK also includes this package.
+2. Install the complete sky130 PDK from [open_pdks](https://github.com/RTimothyEdwards/open_pdks) either manually or with [volare](https://github.com/efabless/volare). The PDK also includes this package.
-When you start KLayout, you must reference this package. This can be done by setting the environment variable `KLAYOUT_HOME`. For example, inside this repository:
+When you start KLayout, you must load this package. This can be done by setting the environment variable `KLAYOUT_PATH`. For example, inside this repository:
```console
-KLAYOUT_HOME=./sky130_tech klayout -e
+KLAYOUT_PATH=./sky130_tech klayout -e
```
### PCells
diff --git a/sky130_tech/tech/sky130/pymacros/cells/cap.py b/sky130_tech/tech/sky130/pymacros/cells/cap.py
index c5aa93dd..aee0a70c 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/cap.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/cap.py
@@ -47,12 +47,12 @@ def __init__(self):
self.Type_handle.default = self.Type_handle.choice_values()[0]
- self.param("l", self.TypeDouble, "length", default=l_min, unit="um")
- self.param("w", self.TypeDouble, "width", default=w_min, unit="um")
- self.param("tap_con_col", self.TypeInt, "tap Contacts Columns", default=1)
+ self.param("l", self.TypeDouble, "Length", default=l_min, unit="um")
+ self.param("w", self.TypeDouble, "Width", default=w_min, unit="um")
+ self.param("tap_con_col", self.TypeInt, "Tap Contacts Columns", default=1)
- self.param("gr", self.TypeBoolean, "Gaurd Ring", default=0)
- self.param("grw", self.TypeDouble, "Gaurd Ring Width", default=grw_min, unit="um")
+ self.param("gr", self.TypeBoolean, "Guard Ring", default=0)
+ self.param("grw", self.TypeDouble, "Guard Ring Width", default=grw_min, unit="um")
self.param("nf", self.TypeDouble, "Number of Fingers", default=1)
#self.param("n", self.TypeDouble, "instance number", default=1)
diff --git a/sky130_tech/tech/sky130/pymacros/cells/diode.py b/sky130_tech/tech/sky130/pymacros/cells/diode.py
index ce4294c6..90442a1d 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/diode.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/diode.py
@@ -139,13 +139,13 @@ def __init__(self):
self.Type_handle.add_choice("sky130_fd_pr__diode_pd2nw_11v0", "sky130_fd_pr__diode_pd2nw_11v0")
self.Type_handle.default = self.Type_handle.choice_values()[0]
- self.param("w", self.TypeDouble, "width", default=d_min, unit="um")
- self.param("l", self.TypeDouble, "length", default=d_min, unit="um")
- self.param("cath_w", self.TypeDouble, "Cathode width", default=grw_min, unit="um")
- self.param("grw", self.TypeDouble, "Gaurd Ring width", default=grw_min, unit="um")
+ self.param("w", self.TypeDouble, "Width", default=d_min, unit="um")
+ self.param("l", self.TypeDouble, "Length", default=d_min, unit="um")
+ self.param("cath_w", self.TypeDouble, "Cathode Width", default=grw_min, unit="um")
+ self.param("grw", self.TypeDouble, "Guard Ring Width", default=grw_min, unit="um")
- self.param("area", self.TypeDouble,"Area", readonly=True, unit="um^2")
- self.param("perim", self.TypeDouble,"Perimeter", readonly=True, unit="um")
+ self.param("area", self.TypeDouble, "Area", readonly=True, unit="um^2")
+ self.param("perim", self.TypeDouble, "Perimeter", readonly=True, unit="um")
#self.param("n", self.TypeInt, "n", default=1)
diff --git a/sky130_tech/tech/sky130/pymacros/cells/draw_fet.py b/sky130_tech/tech/sky130/pymacros/cells/draw_fet.py
index 10eb41ca..82e60723 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/draw_fet.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/draw_fet.py
@@ -47,7 +47,7 @@ def draw_pfet(
) -> gf.Component:
'''
- Retern pfet
+ Return pfet
Args:
cell : kdb.Cell cell to place layout into
@@ -57,7 +57,7 @@ def draw_pfet(
inter_sd_l : Float of source and drain diffusion length between fingers
nf : integer of number of fingers
M : integer of number of multipliers
- grw : gaurd ring width when enabled
+ grw : guard ring width when enabled
type : string of the device type
bulk : String of bulk connection type (None, Bulk Tie, Guard Ring)
con_bet_fin : boolean of having contacts for diffusion between fingers
@@ -384,7 +384,7 @@ def draw_pfet(
- elif bulk == "Gaurd Ring":
+ elif bulk == "guard ring":
psdm = c_inst.add_ref(gf.components.rectangle(size=(l_d+ 2*diff_psdm_enc, w+ 2*diff_psdm_enc),layer= psdm_layer))
psdm.move((-diff_psdm_enc,-diff_psdm_enc))
@@ -394,7 +394,6 @@ def draw_pfet(
rect_bulk_in = c_temp.add_ref(gf.components.rectangle(size=((c_inst.xmax - c_inst.xmin) + 2*diff_tap_spacing,
(c_inst.ymax - c_inst.ymin) + 2*poly_tap_spacing )
, layer= tap_layer))
-
rect_bulk_in.move((c_inst.xmin-diff_tap_spacing,c_inst.ymin - poly_tap_spacing))
rect_bulk_out = c_temp.add_ref(gf.components.rectangle(size=((rect_bulk_in.xmax - rect_bulk_in.xmin) + 2*grw,(rect_bulk_in.ymax - rect_bulk_in.ymin) + 2*grw )
, layer= tap_layer))
@@ -409,7 +408,7 @@ def draw_pfet(
nsdm_out.move((rect_bulk_out.xmin - tap_nsdm_enc, rect_bulk_out.ymin - tap_nsdm_enc))
nsdm = c.add_ref(gf.boolean(A= nsdm_out , B = nsdm_in , operation= "A-B", layer= nsdm_layer) )
- # adding contacts
+ # generating contacts
if grw < licon_size[0] + 2*licon_t_enc :
g_con_range = (B.ymin , B.ymax )
@@ -422,20 +421,20 @@ def draw_pfet(
ring_con_up = c.add_ref(via_generator(x_range=(rect_bulk_in.xmin+0.17,rect_bulk_in.xmax-0.17),y_range=(rect_bulk_in.ymax,rect_bulk_out.ymax)
, via_enclosure=licon_dt_enc, via_layer=licon_layer,via_size=licon_size,via_spacing=licon_spacing))
- ring_con_r = c.add_ref(via_generator(x_range=(rect_bulk_out.xmin,rect_bulk_in.xmin),y_range=g_con_range
+ ring_con_r = c.add_ref(via_generator(x_range=(rect_bulk_out.xmin,rect_bulk_in.xmin),y_range=(rect_bulk_in.ymin+0.17,rect_bulk_in.ymax-0.17)
, via_enclosure=licon_dt_enc, via_layer=licon_layer,via_size=licon_size,via_spacing=licon_spacing))
- ring_con_l = c.add_ref(via_generator(x_range=(rect_bulk_in.xmax,rect_bulk_out.xmax),y_range=g_con_range
+ ring_con_l = c.add_ref(via_generator(x_range=(rect_bulk_in.xmax,rect_bulk_out.xmax),y_range=(rect_bulk_in.ymin+0.17,rect_bulk_in.ymax-0.17)
, via_enclosure=licon_dt_enc, via_layer=licon_layer,via_size=licon_size,via_spacing=licon_spacing))
- tap_li_in = c_temp.add_ref(gf.components.rectangle(size=((l_d ) + 2*diff_tap_spacing,
+ tap_li_in = c_temp.add_ref(gf.components.rectangle(size=((c_inst.xmax - c_inst.xmin) + 2*diff_tap_spacing,
(c_inst.ymax - c_inst.ymin) + 2*poly_tap_spacing )
, layer= li_layer))
- tap_li_in.move((-diff_tap_spacing,c_inst.ymin - poly_tap_spacing))
+ tap_li_in.move((c_inst.xmin - diff_tap_spacing, c_inst.ymin - poly_tap_spacing))
tap_li_out = c_temp.add_ref(gf.components.rectangle(size=((rect_bulk_in.xmax - rect_bulk_in.xmin) + 2*grw,(rect_bulk_in.ymax - rect_bulk_in.ymin) + 2*grw )
, layer= li_layer))
tap_li_out.move((rect_bulk_in.xmin - grw , rect_bulk_in.ymin -grw ))
- li = c.add_ref(gf.boolean(A= rect_bulk_out , B = rect_bulk_in , operation= "A-B", layer= li_layer) )
+ li = c.add_ref(gf.boolean(A= tap_li_out , B = tap_li_in , operation= "A-B", layer= li_layer) )
# generating nwell
@@ -447,7 +446,7 @@ def draw_pfet(
- if bulk != "Gaurd Ring":
+ if bulk != "guard ring":
c.add_ref(c_inst)
# nwell generation
@@ -496,7 +495,7 @@ def draw_nfet(
) : #-> gf.Component:
'''
- Retern nfet
+ Return nfet
Args:
cell : kdb.Cell cell to place layout into
@@ -506,7 +505,7 @@ def draw_nfet(
inter_sd_l : Float of source and drain diffusion length between fingers
nf : integer of number of fingers
M : integer of number of multipliers
- grw : gaurd ring width when enabled
+ grw : guard ring width when enabled
type : string of the device type
bulk : String of bulk connection type (None, Bulk Tie, Guard Ring)
con_bet_fin : boolean of having contacts for diffusion between fingers
@@ -840,7 +839,7 @@ def draw_nfet(
- elif bulk == "Gaurd Ring":
+ elif bulk == "guard ring":
nsdm = c_inst.add_ref(gf.components.rectangle(size=(l_d+ 2*diff_nsdm_enc, w+ 2*diff_nsdm_enc),layer= nsdm_layer))
nsdm.move((-diff_nsdm_enc,-diff_nsdm_enc))
@@ -860,7 +859,7 @@ def draw_nfet(
, layer= psdm_layer))
psdm_in.move((rect_bulk_in.xmin + tap_psdm_enc, rect_bulk_in.ymin + tap_psdm_enc))
psdm_out = c_temp.add_ref(gf.components.rectangle(size=((rect_bulk_out.xmax - rect_bulk_out.xmin) + 2*tap_psdm_enc, (rect_bulk_out.ymax - rect_bulk_out.ymin) + 2*tap_psdm_enc )
- , layer= nsdm_layer))
+ , layer= psdm_layer))
psdm_out.move((rect_bulk_out.xmin - tap_psdm_enc, rect_bulk_out.ymin - tap_psdm_enc))
psdm = c.add_ref(gf.boolean(A= psdm_out , B = psdm_in , operation= "A-B", layer= psdm_layer) )
@@ -881,14 +880,17 @@ def draw_nfet(
ring_con_l = c.add_ref(via_generator(x_range=(rect_bulk_in.xmax,rect_bulk_out.xmax),y_range=(rect_bulk_in.ymin+0.17,rect_bulk_in.ymax-0.17)
, via_enclosure=licon_dt_enc, via_layer=licon_layer,via_size=licon_size,via_spacing=licon_spacing))
- tap_li_in = c_temp.add_ref(gf.components.rectangle(size=((l_d ) + 2*diff_tap_spacing,
- (c_inst.ymax - c_inst.ymin) + 2*poly_tap_spacing )
- , layer= li_layer))
- tap_li_in.move((-diff_tap_spacing,c_inst.ymin - poly_tap_spacing))
+ tap_li_in = c_temp.add_ref(gf.components.rectangle(size=(
+ (c_inst.xmax - c_inst.xmin) + 2*diff_tap_spacing,
+ (c_inst.ymax - c_inst.ymin) + 2*poly_tap_spacing ),
+ layer= li_layer
+ )
+ )
+ tap_li_in.move((c_inst.xmin - diff_tap_spacing, c_inst.ymin - poly_tap_spacing))
tap_li_out = c_temp.add_ref(gf.components.rectangle(size=((rect_bulk_in.xmax - rect_bulk_in.xmin) + 2*grw,(rect_bulk_in.ymax - rect_bulk_in.ymin) + 2*grw )
, layer= li_layer))
tap_li_out.move((rect_bulk_in.xmin - grw , rect_bulk_in.ymin -grw ))
- li = c.add_ref(gf.boolean(A= rect_bulk_out , B = rect_bulk_in , operation= "A-B", layer= li_layer) )
+ li = c.add_ref(gf.boolean(A= tap_li_out , B = tap_li_in , operation= "A-B", layer= li_layer) )
@@ -904,7 +906,7 @@ def draw_nfet(
- if bulk != "Gaurd Ring":
+ if bulk != "guard ring":
c.add_ref(c_inst)
diff --git a/sky130_tech/tech/sky130/pymacros/cells/draw_guard_ring.py b/sky130_tech/tech/sky130/pymacros/cells/draw_guard_ring.py
index 4aafaee5..2a28a458 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/draw_guard_ring.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/draw_guard_ring.py
@@ -28,7 +28,8 @@ def draw_gr (
in_l : float = 1,
in_w : float = 1,
grw : float = 0.17,
- con_lev = "li"
+ con_lev = "li",
+ implant_type = "None"
) :
'''
@@ -44,9 +45,26 @@ def draw_gr (
con_spacing = (0.19, 0.19)
con_enc = (0.12, 0.12)
+ tap_nsdm_enc : float = 0.125
+ tap_psdm_enc : float = 0.125
+
c = open_component("sky_ring_gen")
c_temp = gf.Component("temp_store")
+ # Choose the implant
+ if implant_type == 'nsdm':
+ implant_layer = nsdm_layer
+ if implant_type == 'psdm':
+ implant_layer = psdm_layer
+
+ # Add the implant layer
+ if implant_type != 'None':
+ implant_in = c_temp.add_ref(gf.components.rectangle(size=(in_w - 2*tap_nsdm_enc, in_l - 2*tap_nsdm_enc), layer=implant_layer))
+ implant_in.move((tap_nsdm_enc, tap_nsdm_enc))
+ implant_out = c_temp.add_ref(gf.components.rectangle(size=(in_w + 2*grw + 2*tap_nsdm_enc, in_l + 2*grw + 2*tap_nsdm_enc), layer=implant_layer))
+ implant_out.move((-grw - tap_nsdm_enc, -grw - tap_nsdm_enc))
+ implant = c.add_ref(gf.boolean(A=implant_out, B=implant_in, operation="A-B", layer=implant_layer))
+
inner = c_temp.add_ref(gf.components.rectangle(size=(in_w, in_l), layer=tap_layer))
outer = c_temp.add_ref(gf.components.rectangle(size=(inner.xmax - inner.xmin + 2*grw , inner.ymax - inner.ymin + 2*grw), layer=tap_layer))
outer.move((-grw, -grw))
@@ -54,6 +72,10 @@ def draw_gr (
gr = c.add_ref(gf.boolean(A=outer, B=inner , operation="A-B", layer=tap_layer))
if con_lev == "li" or con_lev == "metal1":
+ inner = c_temp.add_ref(gf.components.rectangle(size=(in_w, in_l), layer=li_layer))
+ outer = c_temp.add_ref(gf.components.rectangle(size=(inner.xmax - inner.xmin + 2*grw , inner.ymax - inner.ymin + 2*grw), layer=li_layer))
+ outer.move((-grw, -grw))
+
li = c.add_ref(gf.boolean(A=outer, B=inner, operation="A-B", layer=li_layer))
if grw < con_size[0] + 2*con_enc[0]:
@@ -73,6 +95,10 @@ def draw_gr (
if con_lev == "metal1" :
+ inner = c_temp.add_ref(gf.components.rectangle(size=(in_w, in_l), layer=m1_layer))
+ outer = c_temp.add_ref(gf.components.rectangle(size=(inner.xmax - inner.xmin + 2*grw , inner.ymax - inner.ymin + 2*grw), layer=m1_layer))
+ outer.move((-grw, -grw))
+
m1 = c.add_ref(gf.boolean(A=outer, B=inner, operation="A-B", layer=m1_layer))
mcon_l = c.add_ref(via_generator(x_range=(outer.xmin, inner.xmin), y_range=(inner.ymin + 0.17 , inner.ymax - 0.17), via_enclosure=con_enc, via_layer=mcon_layer
diff --git a/sky130_tech/tech/sky130/pymacros/cells/fet.py b/sky130_tech/tech/sky130/pymacros/cells/fet.py
index 05154638..0f2c8b61 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/fet.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/fet.py
@@ -57,7 +57,7 @@ def __init__(self):
self.Type_handle = self.param("bulk", self.TypeString, "Bulk Type")
self.Type_handle.add_choice("None", "None")
self.Type_handle.add_choice("bulk tie", "bulk tie")
- self.Type_handle.add_choice("Gaurd Ring", "Gaurd Ring")
+ self.Type_handle.add_choice("guard ring", "guard ring")
self.Type_handle.default = self.Type_handle.choice_values()[0]
self.Type_handle = self.param("gate_con_pos", self.TypeString, "Gate Contact Position")
@@ -68,7 +68,7 @@ def __init__(self):
- self.param("l", self.TypeDouble, "length", default=fet_01v8_l, unit="um")
+ self.param("l", self.TypeDouble, "Length", default=fet_01v8_l, unit="um")
self.param("w", self.TypeDouble, "Width", default=fet_w, unit="um")
self.param("sd_con_col", self.TypeInt, "Diffusion Contacts Columns", default=1)
self.param("inter_sd_l", self.TypeDouble, "Between Fingers Diffusion Length", default=fet_inter_ld, unit="um")
@@ -174,7 +174,7 @@ def __init__(self):
self.Type_handle = self.param("bulk", self.TypeString, "Bulk Type")
self.Type_handle.add_choice("None", "None")
self.Type_handle.add_choice("bulk tie", "bulk tie")
- self.Type_handle.add_choice("Gaurd Ring", "Gaurd Ring")
+ self.Type_handle.add_choice("guard ring", "guard ring")
self.Type_handle.default = self.Type_handle.choice_values()[0]
self.Type_handle = self.param("gate_con_pos", self.TypeString, "Gate Contact Position")
diff --git a/sky130_tech/tech/sky130/pymacros/cells/gr.py b/sky130_tech/tech/sky130/pymacros/cells/gr.py
index 87eaf3af..ac107437 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/gr.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/gr.py
@@ -45,6 +45,12 @@ def __init__(self):
self.Type_handle.add_choice("li", "li")
self.Type_handle.add_choice("metal1", "metal1")
self.Type_handle.default = self.Type_handle.choice_values()[0]
+
+ self.Type_handle = self.param("implant_type", self.TypeString, "Implant Type")
+ self.Type_handle.add_choice("None", "None")
+ self.Type_handle.add_choice("psdm", "psdm")
+ self.Type_handle.add_choice("nsdm", "nsdm")
+ self.Type_handle.default = self.Type_handle.choice_values()[0]
def display_text_impl(self):
# Provide a descriptive text for the cell
@@ -94,5 +100,5 @@ def transformation_from_shape_impl(self):
return pya.Trans(self.shape.bbox().center())
def produce_impl(self):
- draw_gr(cell=self.cell, in_l=self.in_l, in_w=self.in_w , grw= self.grw , con_lev=self.con_lev)
+ draw_gr(cell=self.cell, in_l=self.in_l, in_w=self.in_w , grw= self.grw , con_lev=self.con_lev, implant_type=self.implant_type)
diff --git a/sky130_tech/tech/sky130/pymacros/cells/pdk.py b/sky130_tech/tech/sky130/pymacros/cells/pdk.py
index 83639c27..69db130c 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/pdk.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/pdk.py
@@ -45,6 +45,10 @@ def take_component(c : gf.Component, target_cell : kdb.Cell):
# TODO: have an API for this
source_cell = c._kdb_cell
+
+ # Since dbu for sky130 is 0.001nm, but the manufacturing grid is 0.005nm
+ # snap to 0.005nm grid to prevent offgrid DRC errors
+ source_cell.layout().scale_and_snap(source_cell, 5, mult=1, div=1)
cm = kdb.CellMapping()
cm.for_single_cell(target_cell, source_cell)
diff --git a/sky130_tech/tech/sky130/pymacros/cells/res_klayout_panel.py b/sky130_tech/tech/sky130/pymacros/cells/res_klayout_panel.py
index e60377e1..072ff37c 100644
--- a/sky130_tech/tech/sky130/pymacros/cells/res_klayout_panel.py
+++ b/sky130_tech/tech/sky130/pymacros/cells/res_klayout_panel.py
@@ -32,10 +32,10 @@ def __init__(self, l_min, w_min):
self.Type_handle = self.param("type", self.TypeString, "Device Type")
- self.param("len", self.TypeDouble, "length", default=l_min, unit="um")
- self.param("w", self.TypeDouble, "width", default=w_min, unit="um")
+ self.param("len", self.TypeDouble, "Length", default=l_min, unit="um")
+ self.param("w", self.TypeDouble, "Width", default=w_min, unit="um")
- self.param("gr", self.TypeBoolean, "Gaurd Ring", default=1)
+ self.param("gr", self.TypeBoolean, "Guard Ring", default=1)
self.param(
"area", self.TypeDouble, "Area", readonly=True, unit="um^2"
)
diff --git a/sky130_tech/tech/sky130/sky130.lyt b/sky130_tech/tech/sky130/sky130.lyt
index 137dcacd..3d6d5471 100644
--- a/sky130_tech/tech/sky130/sky130.lyt
+++ b/sky130_tech/tech/sky130/sky130.lyt
@@ -24,6 +24,7 @@
$PDK_ROOT/$PDK/libs.tech/klayout
sky130.lyp
true
+ 0.01,0.005!
1