From 9bfaf23d7ac1cf90a7fa7d64759903b56936f0fa Mon Sep 17 00:00:00 2001 From: mole99 Date: Wed, 7 Aug 2024 16:02:45 +0200 Subject: [PATCH 1/4] Use KLAYOUT_PATH instead of KLAYOUT_HOME --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8d5e4cc..20a0d94a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ You have two options for using 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: ```console -KLAYOUT_HOME=./sky130_tech klayout -e +KLAYOUT_PATH=./sky130_tech klayout -e ``` ### PCells From 264a068f1321198c48f3caa701bbd3c3f6b0b6c0 Mon Sep 17 00:00:00 2001 From: mole99 Date: Wed, 7 Aug 2024 16:02:59 +0200 Subject: [PATCH 2/4] Improve pcells --- sky130_tech/tech/sky130/pymacros/cells/cap.py | 10 ++--- .../tech/sky130/pymacros/cells/diode.py | 12 ++--- .../tech/sky130/pymacros/cells/draw_fet.py | 44 ++++++++++--------- .../sky130/pymacros/cells/draw_guard_ring.py | 28 +++++++++++- sky130_tech/tech/sky130/pymacros/cells/fet.py | 6 +-- sky130_tech/tech/sky130/pymacros/cells/gr.py | 8 +++- sky130_tech/tech/sky130/pymacros/cells/pdk.py | 4 ++ .../pymacros/cells/res_klayout_panel.py | 6 +-- 8 files changed, 78 insertions(+), 40 deletions(-) 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" ) From ca84d626f8d83c07d577aa74eb49ebe2da0f530e Mon Sep 17 00:00:00 2001 From: mole99 Date: Wed, 7 Aug 2024 16:19:18 +0200 Subject: [PATCH 3/4] Add default grid, set to 0.005 --- sky130_tech/tech/sky130/sky130.lyt | 1 + 1 file changed, 1 insertion(+) 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 From 617395b4c161e6a5c230c940f967ac5724b7586f Mon Sep 17 00:00:00 2001 From: mole99 Date: Wed, 7 Aug 2024 16:19:30 +0200 Subject: [PATCH 4/4] Fixup --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 20a0d94a..b3ad96f9 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ 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_PATH=./sky130_tech klayout -e