diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
index 275c512b..f5578329 100644
--- a/.github/workflows/linting.yml
+++ b/.github/workflows/linting.yml
@@ -21,12 +21,20 @@ on:
jobs:
lint_python:
runs-on: ubuntu-latest
- strategy:
- max-parallel: 12
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Lint with flake8
run: |
- make lint
+ make lint_python
+
+ lint_ruby:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: 'recursive'
+ - name: Lint with rubocop
+ run: |
+ make lint_ruby
diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml
index 6b7678f0..e222aae4 100644
--- a/.github/workflows/regression.yml
+++ b/.github/workflows/regression.yml
@@ -45,7 +45,6 @@ jobs:
needs: build_drc-matrix
runs-on: ubuntu-latest
strategy:
- max-parallel: 4
fail-fast: false
matrix:
part: [drc]
@@ -64,7 +63,6 @@ jobs:
drc_switch:
runs-on: ubuntu-latest
strategy:
- max-parallel: 4
fail-fast: false
matrix:
include:
@@ -83,7 +81,6 @@ jobs:
lvs_regression:
runs-on: ubuntu-latest
strategy:
- max-parallel: 4
fail-fast: false
matrix:
include:
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 00000000..ca447691
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,45 @@
+# The behavior of RuboCop can be controlled via the .rubocop.yml
+# configuration file. It makes it possible to enable/disable
+# certain cops (checks) and to alter their behavior if they accept
+# any parameters. The file can be placed either in your home
+# directory or in some project directory.
+#
+# RuboCop will start looking for the configuration file in the directory
+# where the inspected file is and continue its way up to the root directory.
+#
+# See https://docs.rubocop.org/rubocop/configuration
+
+Style/FrozenStringLiteralComment:
+ Enabled: false
+
+GlobalVars:
+ Description: Do not introduce global variables.
+ Enabled: false
+
+UselessAssignment:
+ Description: Useless assignment to variable.
+ Enabled: false
+
+Metrics/AbcSize:
+ Description: Assignment Branch Condition size for conn_space is too high.
+ Enabled: false
+
+Metrics/MethodLength:
+ Description: Method has too many lines.
+ Enabled: false
+
+RSpec/VariableName:
+ Description: Use snake_case for variable names.
+ EnforcedStyle: "snake_case"
+ Enabled: false
+
+Metrics/BlockNesting:
+ Description: Avoid more than 3 levels of block nesting.
+ Enabled: false
+
+AllCops:
+ Exclude:
+ - 'env/**/*'
+ Include:
+ - '**/*.rb'
+ - '**/*.drc'
diff --git a/Makefile b/Makefile
index 21f134b2..87c50bbd 100644
--- a/Makefile
+++ b/Makefile
@@ -25,12 +25,19 @@ ENVIRONMENT_FILE := pdk_regression.yml
# Path to regression
KLAYOUT_TESTS := klayout/drc/testing/
+
include third_party/make-env/conda.mk
# Lint python code
-lint: | $(CONDA_ENV_PYTHON)
+lint_python: | $(CONDA_ENV_PYTHON)
@$(IN_CONDA_ENV) flake8 .
+# Lint ruby code
+lint_ruby:| $(CONDA_ENV_PYTHON)
+ @$(IN_CONDA_ENV) gem install rubocop
+ @ln -s $$CONDA_PREFIX/bin/ruby $$CONDA_PREFIX/share/rubygems/bin/ruby
+ @$(IN_CONDA_ENV) rubocop .
+
################################################################################
## DRC Regression section
################################################################################
diff --git a/klayout/drc/rule_decks/drc_bjt.drc b/klayout/drc/rule_decks/drc_bjt.drc
deleted file mode 100644
index e6db3a5e..00000000
--- a/klayout/drc/rule_decks/drc_bjt.drc
+++ /dev/null
@@ -1,41 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-
-if FEOL
- #================================================
- #--------------------DRC_BJT---------------------
- #================================================
-
- # Rule BJT.1: Min. DRC_BJT overlap of DNWELL for NPN BJT.
- logger.info("Executing rule BJT.1")
- bjt1_l1 = dnwell.interacting(drc_bjt).not(dnwell.inside(drc_bjt))
- bjt1_l1.output("BJT.1", "BJT.1 : Min. DRC_BJT overlap of DNWELL for NPN BJT.")
- bjt1_l1.forget
-
- # Rule BJT.2: Min. DRC_BJT overlap of PCOM in Psub.
- logger.info("Executing rule BJT.2")
- bjt2_l1 = pcomp.outside(nwell).outside(dnwell).interacting(drc_bjt).not(pcomp.outside(nwell).outside(dnwell).inside(drc_bjt))
- bjt2_l1.output("BJT.2", "BJT.2 : Min. DRC_BJT overlap of PCOM in Psub.")
- bjt2_l1.forget
-
- # Rule BJT.3: Minimum space of DRC_BJT layer to unrelated COMP. is 0.1µm
- logger.info("Executing rule BJT.3")
- bjt3_l1 = comp.outside(drc_bjt).separation(drc_bjt, 0.1.um, euclidian).polygons(0.001)
- bjt3_l1.output("BJT.3", "BJT.3 : Minimum space of DRC_BJT layer to unrelated COMP. : 0.1µm")
- bjt3_l1.forget
-end #FEOL
-
diff --git a/klayout/drc/rule_decks/dualgate.drc b/klayout/drc/rule_decks/dualgate.drc
index 871a7a6e..0490b8b9 100644
--- a/klayout/drc/rule_decks/dualgate.drc
+++ b/klayout/drc/rule_decks/dualgate.drc
@@ -15,74 +15,73 @@
################################################################################################
if FEOL
- #================================================
- #--------------------DUALGATE--------------------
- #================================================
+ #================================================
+ #--------------------DUALGATE--------------------
+ #================================================
- # Rule DV.1: Min. Dualgate enclose DNWELL. is 0.5µm
- logger.info("Executing rule DV.1")
- dv1_l1 = dualgate.enclosing(dnwell, 0.5.um, euclidian).polygons(0.001)
- dv1_l2 = dnwell.not_outside(dualgate).not(dualgate)
- dv1_l = dv1_l1.or(dv1_l2)
- dv1_l.output("DV.1", "DV.1 : Min. Dualgate enclose DNWELL. : 0.5µm")
- dv1_l1.forget
- dv1_l2.forget
- dv1_l.forget
+ # Rule DV.1: Min. Dualgate enclose DNWELL. is 0.5µm
+ logger.info('Executing rule DV.1')
+ dv1_l1 = dualgate.enclosing(dnwell, 0.5.um, euclidian).polygons(0.001)
+ dv1_l2 = dnwell.not_outside(dualgate).not(dualgate)
+ dv1_l = dv1_l1.or(dv1_l2)
+ dv1_l.output('DV.1', 'DV.1 : Min. Dualgate enclose DNWELL. : 0.5µm')
+ dv1_l1.forget
+ dv1_l2.forget
+ dv1_l.forget
- # Rule DV.2: Min. Dualgate Space. Merge if Space is less than this design rule. is 0.44µm
- logger.info("Executing rule DV.2")
- dv2_l1 = dualgate.space(0.44.um, euclidian).polygons(0.001)
- dv2_l1.output("DV.2", "DV.2 : Min. Dualgate Space. Merge if Space is less than this design rule. : 0.44µm")
- dv2_l1.forget
+ # Rule DV.2: Min. Dualgate Space. Merge if Space is less than this design rule. is 0.44µm
+ logger.info('Executing rule DV.2')
+ dv2_l1 = dualgate.space(0.44.um, euclidian).polygons(0.001)
+ dv2_l1.output('DV.2', 'DV.2 : Min. Dualgate Space. Merge if Space is less than this design rule. : 0.44µm')
+ dv2_l1.forget
- # Rule DV.3: Min. Dualgate to COMP space [unrelated]. is 0.24µm
- logger.info("Executing rule DV.3")
- dv3_l1 = dualgate.separation(comp.outside(dualgate), 0.24.um, euclidian).polygons(0.001)
- dv3_l1.output("DV.3", "DV.3 : Min. Dualgate to COMP space [unrelated]. : 0.24µm")
- dv3_l1.forget
+ # Rule DV.3: Min. Dualgate to COMP space [unrelated]. is 0.24µm
+ logger.info('Executing rule DV.3')
+ dv3_l1 = dualgate.separation(comp.outside(dualgate), 0.24.um, euclidian).polygons(0.001)
+ dv3_l1.output('DV.3', 'DV.3 : Min. Dualgate to COMP space [unrelated]. : 0.24µm')
+ dv3_l1.forget
- # rule DV.4 is not a DRC check
- # Refer to: https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_07_07.html
+ # rule DV.4 is not a DRC check
+ # Refer to: https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_07_07.html
- # Rule DV.5: Min. Dualgate width. is 0.7µm
- logger.info("Executing rule DV.5")
- dv5_l1 = dualgate.width(0.7.um, euclidian).polygons(0.001)
- dv5_l1.output("DV.5", "DV.5 : Min. Dualgate width. : 0.7µm")
- dv5_l1.forget
+ # Rule DV.5: Min. Dualgate width. is 0.7µm
+ logger.info('Executing rule DV.5')
+ dv5_l1 = dualgate.width(0.7.um, euclidian).polygons(0.001)
+ dv5_l1.output('DV.5', 'DV.5 : Min. Dualgate width. : 0.7µm')
+ dv5_l1.forget
- comp_dv = comp.not(pcomp.outside(nwell))
- # Rule DV.6: Min. Dualgate enclose COMP (except substrate tap). is 0.24µm
- logger.info("Executing rule DV.6")
- dv6_l1 = dualgate.enclosing(comp_dv, 0.24.um, euclidian).polygons(0.001)
- dv6_l2 = comp_dv.not_outside(dualgate).not(dualgate)
- dv6_l = dv6_l1.or(dv6_l2)
- dv6_l.output("DV.6", "DV.6 : Min. Dualgate enclose COMP (except substrate tap). : 0.24µm")
- dv6_l1.forget
- dv6_l2.forget
- dv6_l.forget
+ comp_dv = comp.not(pcomp.outside(nwell))
+ # Rule DV.6: Min. Dualgate enclose COMP (except substrate tap). is 0.24µm
+ logger.info('Executing rule DV.6')
+ dv6_l1 = dualgate.enclosing(comp_dv, 0.24.um, euclidian).polygons(0.001)
+ dv6_l2 = comp_dv.not_outside(dualgate).not(dualgate)
+ dv6_l = dv6_l1.or(dv6_l2)
+ dv6_l.output('DV.6', 'DV.6 : Min. Dualgate enclose COMP (except substrate tap). : 0.24µm')
+ dv6_l1.forget
+ dv6_l2.forget
+ dv6_l.forget
- # Rule DV.7: COMP (except substrate tap) can not be partially overlapped by Dualgate.
- logger.info("Executing rule DV.7")
- dv7_l1 = dualgate.not_outside(comp_dv).not(dualgate.covering(comp_dv))
- dv7_l1.output("DV.7", "DV.7 : COMP (except substrate tap) can not be partially overlapped by Dualgate.")
- dv7_l1.forget
+ # Rule DV.7: COMP (except substrate tap) can not be partially overlapped by Dualgate.
+ logger.info('Executing rule DV.7')
+ dv7_l1 = dualgate.not_outside(comp_dv).not(dualgate.covering(comp_dv))
+ dv7_l1.output('DV.7', 'DV.7 : COMP (except substrate tap) can not be partially overlapped by Dualgate.')
+ dv7_l1.forget
- comp_dv.forget
+ comp_dv.forget
- # Rule DV.8: Min Dualgate enclose Poly2. is 0.4µm
- logger.info("Executing rule DV.8")
- dv8_l1 = dualgate.enclosing(poly2, 0.4.um, euclidian).polygons(0.001)
- dv8_l2 = poly2.not_outside(dualgate).not(dualgate)
- dv8_l = dv8_l1.or(dv8_l2)
- dv8_l.output("DV.8", "DV.8 : Min Dualgate enclose Poly2. : 0.4µm")
- dv8_l1.forget
- dv8_l2.forget
- dv8_l.forget
-
- # Rule DV.9: 3.3V and 5V/6V PMOS cannot be sitting inside same NWELL.
- logger.info("Executing rule DV.9")
- dv9_l1 = nwell.covering(pgate.and(dualgate)).covering(pgate.not_inside(v5_xtor).not_inside(dualgate))
- dv9_l1.output("DV.9", "DV.9 : 3.3V and 5V/6V PMOS cannot be sitting inside same NWELL.")
- dv9_l1.forget
-end #FEOL
+ # Rule DV.8: Min Dualgate enclose Poly2. is 0.4µm
+ logger.info('Executing rule DV.8')
+ dv8_l1 = dualgate.enclosing(poly2, 0.4.um, euclidian).polygons(0.001)
+ dv8_l2 = poly2.not_outside(dualgate).not(dualgate)
+ dv8_l = dv8_l1.or(dv8_l2)
+ dv8_l.output('DV.8', 'DV.8 : Min Dualgate enclose Poly2. : 0.4µm')
+ dv8_l1.forget
+ dv8_l2.forget
+ dv8_l.forget
+ # Rule DV.9: 3.3V and 5V/6V PMOS cannot be sitting inside same NWELL.
+ logger.info('Executing rule DV.9')
+ dv9_l1 = nwell.covering(pgate.and(dualgate)).covering(pgate.not_inside(v5_xtor).not_inside(dualgate))
+ dv9_l1.output('DV.9', 'DV.9 : 3.3V and 5V/6V PMOS cannot be sitting inside same NWELL.')
+ dv9_l1.forget
+end
diff --git a/klayout/drc/rule_decks/dummy_exclude.drc b/klayout/drc/rule_decks/dummy_exclude.drc
deleted file mode 100644
index ed3c4656..00000000
--- a/klayout/drc/rule_decks/dummy_exclude.drc
+++ /dev/null
@@ -1,43 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-#================================================
-#--------------DUMMY EXCLUDE LAYERS--------------
-#================================================
-
-# rule DE.1 is not a DRC check
-## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_08.html#design-rules-for-dummy-exclude-layers-ndmy-and-pmndmy
-
-# Rule DE.2: Minimum NDMY or PMNDMY size (x or y dimension in um). is 0.8µm
-logger.info("Executing rule DE.2")
-de2_l1 = ndmy.or(pmndmy).width(0.8.um, euclidian)
-de2_l1.output("DE.2", "DE.2 : Minimum NDMY or PMNDMY size (x or y dimension in um). : 0.8µm")
-de2_l1.forget
-
-# Rule DE.3: If size greater than 15000 um2 then two sides should not be greater than (80 um).
-logger.info("Executing rule DE.3")
-de3_ndmy_area = ndmy.with_area(15000.um, nil)
-de3_l1 = de3_ndmy_area.edges.with_length(80.001.um, nil)
-de3_l1.output("DE.3", "DE.3 : If size greater than 15000 um2 then two sides should not be greater than (80 um).")
-de3_l1.forget
-de3_ndmy_area.forget
-
-# Rule DE.4: Minimum NDMY to NDMY space (Merge if space is less). is 20µm
-logger.info("Executing rule DE.4")
-de4_l1 = ndmy.space(20.um, euclidian)
-de4_l1.output("DE.4", "DE.4 : Minimum NDMY to NDMY space (Merge if space is less). : 20µm")
-de4_l1.forget
-
diff --git a/klayout/drc/rule_decks/efuse.drc b/klayout/drc/rule_decks/efuse.drc
deleted file mode 100644
index ccaf55a0..00000000
--- a/klayout/drc/rule_decks/efuse.drc
+++ /dev/null
@@ -1,201 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-
-#================================================
-#---------------------EFUSE----------------------
-#================================================
-
-
-logger.info("Starting EFUSE derivations")
-plfuse_efuse = plfuse.and(efuse_mk)
-
-# Rule EF.01: Min. (Poly2 butt PLFUSE) within EFUSE_MK and Pplus.
-logger.info("Executing rule EF.01")
-ef01_l1 = poly2.or(plfuse).interacting(efuse_mk).not_inside(efuse_mk.and(pplus))
-ef01_l1.output("EF.01", "EF.01 : Min. (Poly2 butt PLFUSE) within EFUSE_MK and Pplus.")
-ef01_l1.forget
-
-# Rule EF.02: Min. Max. PLFUSE width. is 0.18µm
-logger.info("Executing rule EF.02")
-ef02_l1 = plfuse.drc(width != 0.18.um)
-ef02_l1.output("EF.02", "EF.02 : Min. Max. PLFUSE width. : 0.18µm")
-ef02_l1.forget
-
-# Rule EF.03: Min. Max. PLFUSE length. is 1.26µm
-logger.info("Executing rule EF.03")
-ef03_l1 = plfuse.edges.interacting(poly2.edges.and(plfuse.edges).centers(0, 0.95)).without_length(1.26.um)
-ef03_l1.output("EF.03", "EF.03 : Min. Max. PLFUSE length. : 1.26µm")
-ef03_l1.forget
-
-# Rule EF.04a: Min. Max. PLFUSE overlap Poly2 (coinciding permitted) and touch cathode and anode.
-logger.info("Executing rule EF.04a")
-ef04a_l1 = plfuse.not_in(plfuse.interacting(poly2.not(plfuse), 2, 2)).inside(efuse_mk).or(plfuse.not(poly2).inside(efuse_mk))
-ef04a_l1.output("EF.04a", "EF.04a : Min. Max. PLFUSE overlap Poly2 (coinciding permitted) and touch cathode and anode.")
-ef04a_l1.forget
-
-# Rule EF.04b: PLFUSE must be rectangular.
-logger.info("Executing rule EF.04b")
-ef04b_l1 = plfuse.non_rectangles
-ef04b_l1.output("EF.04b", "EF.04b : PLFUSE must be rectangular.")
-ef04b_l1.forget
-
-# Rule EF.04c: Cathode Poly2 must be rectangular.
-logger.info("Executing rule EF.04c")
-cathode = poly2.and(efuse_mk).not(lvs_source.or(plfuse))
-ef04c_l1 = cathode.non_rectangles
-ef04c_l1.output("EF.04c", "EF.04c : Cathode Poly2 must be rectangular.")
-ef04c_l1.forget
-
-# Rule EF.04d: Anode Poly2 must be rectangular.
-logger.info("Executing rule EF.04d")
-anode = poly2.and(lvs_source).and(efuse_mk)
-ef04d_l1 = anode.non_rectangles
-ef04d_l1.output("EF.04d", "EF.04d : Anode Poly2 must be rectangular.")
-ef04d_l1.forget
-
-# Rule EF.05: Min./Max. LVS_Source overlap Poly2 (at Anode).
-logger.info("Executing rule EF.05")
-ef05_l1 = poly2.not(plfuse).interacting(lvs_source).not(lvs_source).and(efuse_mk).or(lvs_source.not(poly2).and(efuse_mk))
-ef05_l1.output("EF.05", "EF.05 : Min./Max. LVS_Source overlap Poly2 (at Anode).")
-ef05_l1.forget
-
-# Rule EF.06: Min./Max. Cathode Poly2 width. is 2.26µm
-logger.info("Executing rule EF.06")
-cathode_width = cathode.edges.not_interacting(cathode.edges.interacting(plfuse)).or(cathode.edges.interacting(plfuse))
-ef06_l1 = cathode_width.without_length(2.26.um)
-ef06_l1.output("EF.06", "EF.06 : Min./Max. Cathode Poly2 width. : 2.26µm")
-ef06_l1.forget
-
-# Rule EF.07: Min./Max. Cathode Poly2 length. is 1.84µm
-logger.info("Executing rule EF.07")
-ef07_l1 = cathode.edges.not(cathode_width).without_length(1.84.um)
-ef07_l1.output("EF.07", "EF.07 : Min./Max. Cathode Poly2 length. : 1.84µm")
-ef07_l1.forget
-
-# Rule EF.08: Min./Max. Anode Poly2 width. is 1.06µm
-logger.info("Executing rule EF.08")
-anode_width = anode.edges.not_interacting(anode.edges.interacting(plfuse)).or(anode.edges.interacting(plfuse))
-ef08_l1 = anode_width.without_length(1.06.um)
-ef08_l1.output("EF.08", "EF.08 : Min./Max. Anode Poly2 width. : 1.06µm")
-ef08_l1.forget
-
-# Rule EF.09: Min./Max. Anode Poly2 length. is 2.43µm
-logger.info("Executing rule EF.09")
-ef09_l1 = anode.edges.not(anode_width).without_length(2.43.um)
-ef09_l1.output("EF.09", "EF.09 : Min./Max. Anode Poly2 length. : 2.43µm")
-ef09_l1.forget
-anode_width.forget
-
-# Rule EF.10: Min. Cathode Poly2 to Poly2 space. is 0.26µm
-logger.info("Executing rule EF.10")
-ef10_l1 = cathode.space(0.26.um, euclidian)
-ef10_l1.output("EF.10", "EF.10 : Min. Cathode Poly2 to Poly2 space. : 0.26µm")
-ef10_l1.forget
-
-# Rule EF.11: Min. Anode Poly2 to Poly2 space. is 0.26µm
-logger.info("Executing rule EF.11")
-ef11_l1 = anode.space(0.26.um, euclidian)
-ef11_l1.output("EF.11", "EF.11 : Min. Anode Poly2 to Poly2 space. : 0.26µm")
-ef11_l1.forget
-
-# Rule EF.12: Min. Space of Cathode Contact to PLFUSE end.
-logger.info("Executing rule EF.12")
-cont_ef = contact.and(plfuse_efuse)
-ef12_l1 = plfuse_efuse.separation(contact.and(cathode), 0.155.um).polygons(0.001).or(cont_ef)
-ef12_l1.output("EF.12", "EF.12 : Min. Space of Cathode Contact to PLFUSE end.")
-ef12_l1.forget
-
-# Rule EF.13: Min. Space of Anode Contact to PLFUSE end.
-logger.info("Executing rule EF.13")
-ef13_l1 = plfuse_efuse.separation(contact.and(anode), 0.14.um).polygons(0.001).or(cont_ef)
-ef13_l1.output("EF.13", "EF.13 : Min. Space of Anode Contact to PLFUSE end.")
-ef13_l1.forget
-cont_ef.forget
-
-# Rule EF.14: Min. EFUSE_MK enclose LVS_Source.
-logger.info("Executing rule EF.14")
-ef14_l1 = lvs_source.not_outside(efuse_mk).not(efuse_mk)
-ef14_l1.output("EF.14", "EF.14 : Min. EFUSE_MK enclose LVS_Source.")
-ef14_l1.forget
-
-# Rule EF.15: NO Contact is allowed to touch PLFUSE.
-logger.info("Executing rule EF.15")
-ef15_l1 = plfuse.interacting(contact)
-ef15_l1.output("EF.15", "EF.15 : NO Contact is allowed to touch PLFUSE.")
-ef15_l1.forget
-
-# Rule EF.16a: Cathode must contain exact number of Contacts at each ends. is 4µm
-logger.info("Executing rule EF.16a")
-ef16a_l1 = cathode.not_covering(contact, 4, 4)
-ef16a_l1.output("EF.16a", "EF.16a : Cathode must contain exact number of Contacts at each ends. : 4µm")
-ef16a_l1.forget
-
-# Rule EF.16b: Anode must contain exact number of Contacts at each ends. is 4µm
-logger.info("Executing rule EF.16b")
-ef16b_l1 = anode.not_covering(contact, 4, 4)
-ef16b_l1.output("EF.16b", "EF.16b : Anode must contain exact number of Contacts at each ends. : 4µm")
-ef16b_l1.forget
-
-# Rule EF.17: Min. Space of EFUSE_MK to EFUSE_MK. is 0.26µm
-logger.info("Executing rule EF.17")
-ef17_l1 = efuse_mk.space(0.26.um, euclidian)
-ef17_l1.output("EF.17", "EF.17 : Min. Space of EFUSE_MK to EFUSE_MK. : 0.26µm")
-ef17_l1.forget
-
-# Rule EF.18: PLFUSE must sit on field oxide (NOT COMP), no cross with any COMP, Nplus, Pplus, ESD, SAB, Resistor, Metal1, Metal2.
-logger.info("Executing rule EF.18")
-ef18_l1 = plfuse.not(plfuse.outside(comp).outside(nplus).outside(esd).outside(sab).outside(resistor).outside(metal1).outside(metal2))
-ef18_l1.output("EF.18", "EF.18 : PLFUSE must sit on field oxide (NOT COMP), no cross with any COMP, Nplus, Pplus, ESD, SAB, Resistor, Metal1, Metal2.")
-ef18_l1.forget
-
-# Rule EF.19: Min. PLFUSE space to Metal1, Metal2.
-logger.info("Executing rule EF.19")
-ef19_l1 = plfuse.not(plfuse.outside(metal1).outside(metal2))
-ef19_l1.output("EF.19", "EF.19 : Min. PLFUSE space to Metal1, Metal2.")
-ef19_l1.forget
-
-# Rule EF.20: Min. PLFUSE space to COMP, Nplus, Pplus, Resistor, ESD, SAB. is 2.73µm
-logger.info("Executing rule EF.20")
-ef20_l1 = plfuse.separation(comp + nplus + esd + sab + resistor, 2.73.um, euclidian)
-ef20_l1.output("EF.20", "EF.20 : Min. PLFUSE space to COMP, Nplus, Pplus, Resistor, ESD, SAB. : 2.73µm")
-ef20_l1.forget
-
-# Rule EF.21: Min./Max. eFUSE Poly2 length. is 5.53µm
-logger.info("Executing rule EF.21")
-ef_21_fuse = poly2.interacting(plfuse).inside(efuse_mk.and(pplus)).extents.edges
-ef_21_anode = anode.edges.not_interacting(anode.edges.interacting(plfuse))
-ef_21_cathode = cathode.edges.not_interacting(cathode.edges.interacting(plfuse))
-ef21_l1 = ef_21_fuse.not_interacting(ef_21_anode.or(ef_21_cathode).centers(0, 0.95)).without_length(5.53.um)
-ef21_l1.output("EF.21", "EF.21 : Min./Max. eFUSE Poly2 length. : 5.53µm")
-ef21_l1.forget
-ef_21_fuse.forget
-ef_21_anode.forget
-ef_21_cathode.forget
-
-# Rule EF.22a: Min./Max. Cathode Poly2 overlap with PLFUSE in width direction. is 1.04µm
-logger.info("Executing rule EF.22a")
-ef22a_l1 = cathode.edges.interacting(plfuse).not(plfuse.edges).without_length(1.04.um)
-ef22a_l1.output("EF.22a", "EF.22a : Min./Max. Cathode Poly2 overlap with PLFUSE in width direction. : 1.04µm")
-ef22a_l1.forget
-
-# Rule EF.22b: Min./Max. Anode Poly2 overlap with PLFUSE in width direction. is 0.44µm
-logger.info("Executing rule EF.22b")
-ef22b_l1 = anode.edges.interacting(plfuse).not(plfuse.edges).without_length(0.44.um)
-ef22b_l1.output("EF.22b", "EF.22b : Min./Max. Anode Poly2 overlap with PLFUSE in width direction. : 0.44µm")
-ef22b_l1.forget
-plfuse_efuse.forget
-
diff --git a/klayout/drc/rule_decks/hres.drc b/klayout/drc/rule_decks/hres.drc
deleted file mode 100644
index fb55001f..00000000
--- a/klayout/drc/rule_decks/hres.drc
+++ /dev/null
@@ -1,137 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-
-if FEOL
- #================================================
- #----------------H POLY RESISTOR-----------------
- #================================================
-
- logger.info("Starting HRES derivations")
- hres_poly = poly2.interacting(pplus).interacting(sab).interacting(res_mk).interacting(resistor)
- hres1_poly = poly2.interacting(pplus).interacting(sab).interacting(res_mk)
-
- # Rule HRES.1: Minimum space. Note : Merge if the spacing is less than 0.4 um. is 0.4µm
- logger.info("Executing rule HRES.1")
- hres1_l1 = resistor.interacting(hres1_poly).space(0.4.um, euclidian)
- hres1_l1.output("HRES.1", "HRES.1 : Minimum space. Note : Merge if the spacing is less than 0.4 um. : 0.4µm")
- hres1_l1.forget
-
- # Rule HRES.2: Minimum width of Poly2 resistor. is 1µm
- logger.info("Executing rule HRES.2")
- hres2_l1 = hres_poly.width(1.um, euclidian)
- hres2_l1.output("HRES.2", "HRES.2 : Minimum width of Poly2 resistor. : 1µm")
- hres2_l1.forget
-
- # Rule HRES.3: Minimum space between Poly2 resistors. is 0.4µm
- logger.info("Executing rule HRES.3")
- hres3_l1 = hres_poly.space(0.4.um, euclidian)
- hres3_l1.output("HRES.3", "HRES.3 : Minimum space between Poly2 resistors. : 0.4µm")
- hres3_l1.forget
-
- # Rule HRES.4: Minimum RESISTOR overlap of Poly2 resistor. is 0.4µm
- logger.info("Executing rule HRES.4")
- hres4_l1 = hres_poly.enclosed(resistor, 0.4.um, euclidian).polygons(0.001)
- hres4_l2 = hres_poly.not_outside(resistor).not(resistor)
- hres4_l = hres4_l1.or(hres4_l2)
- hres4_l.output("HRES.4", "HRES.4 : Minimum RESISTOR overlap of Poly2 resistor. : 0.4µm")
- hres4_l1.forget
- hres4_l2.forget
- hres4_l.forget
-
- # Rule HRES.5: Minimum RESISTOR space to unrelated Poly2. is 0.3µm
- logger.info("Executing rule HRES.5")
- hres5_l1 = resistor.interacting(hres1_poly).separation(poly2.not_interacting(sab), 0.3.um, euclidian)
- hres5_l1.output("HRES.5", "HRES.5 : Minimum RESISTOR space to unrelated Poly2. : 0.3µm")
- hres5_l1.forget
-
- # Rule HRES.6: Minimum RESISTOR space to COMP.
- logger.info("Executing rule HRES.6")
- hres6_l1 = resistor.interacting(hres1_poly).separation(comp, 0.3.um, euclidian).polygons(0.001)
- hres6_l2 = comp.not_outside(resistor.interacting(hres1_poly))
- hres6_l = hres6_l1.or(hres6_l2)
- hres6_l.output("HRES.6", "HRES.6 : Minimum RESISTOR space to COMP.")
- hres6_l.forget
- hres6_l1.forget
- hres6_l2.forget
- hres1_poly.forget
-
- # Rule HRES.7: Minimum Pplus overlap of contact on Poly2 resistor. is 0.2µm
- logger.info("Executing rule HRES.7")
- hres7_l1 = contact.and(hres_poly).enclosed(pplus, 0.2.um, euclidian).polygons(0.001)
- hres7_l2 = contact.and(hres_poly).not_outside(pplus).not(pplus)
- hres7_l = hres7_l1.or(hres7_l2)
- hres7_l.output("HRES.7", "HRES.7 : Minimum Pplus overlap of contact on Poly2 resistor. : 0.2µm")
- hres7_l1.forget
- hres7_l2.forget
- hres7_l.forget
-
- # Rule HRES.8: Space from salicide block to contact on Poly2 resistor.
- logger.info("Executing rule HRES.8")
- hres8_l1 = contact.and(hres_poly).separation(sab,0.22.um).polygons(0.001).or(contact.and(hres_poly).interacting(sab))
- hres8_l1.output("HRES.8", "HRES.8 : Space from salicide block to contact on Poly2 resistor.")
- hres8_l1.forget
-
- # Rule HRES.9: Minimum salicide block overlap of Poly2 resistor in width direction.
- logger.info("Executing rule HRES.9")
- hres9_sab = sab.interacting(pplus).interacting(res_mk).interacting(resistor)
- hres9_clear_sab = hres9_sab.not(hres_poly)
- hres9_bad_inside_edge = hres9_sab.edges.inside_part(hres_poly).extended(0,0,0.001,0.001).interacting(hres9_clear_sab, 1, 1)
- hres9_sab_hole = hres9_sab.holes.and(hres_poly)
- hres9_l1 = hres_poly.enclosed(hres9_sab, 0.28.um, euclidian).polygons(0.001)
- hres9_l2 = hres9_bad_inside_edge.or(hres9_sab_hole)
- hres9_l = hres9_l1.or(hres9_l2)
- hres9_l.output("HRES.9", "HRES.9 : Minimum salicide block overlap of Poly2 resistor in width direction.")
- hres9_l.forget
- hres9_l1.forget
- hres9_l2.forget
- hres9_sab.forget
- hres9_clear_sab.forget
- hres9_bad_inside_edge.forget
- hres9_sab_hole.forget
-
- # Rule HRES.10: Minimum & maximum Pplus overlap of SAB.
- logger.info("Executing rule HRES.10")
- pplus1_hres10 = pplus.and(sab).drc(width != 0.1.um)
- pplus2_hres10 = pplus.not_overlapping(sab).edges
- hres10_l1 = pplus1_hres10.or(pplus2_hres10).extended(0, 0, 0.001, 0.001).interacting(hres_poly)
- hres10_l1.output("HRES.10", "HRES.10 : Minimum & maximum Pplus overlap of SAB.")
- hres10_l1.forget
- pplus1_hres10.forget
- pplus2_hres10.forget
-
- # Rule HRES.11 is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_03.html#hres-poly-resistor-phres-optional-with-one-additional-mask
-
- # Rule HRES.12a: P type Poly2 resistor (high sheet rho) shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by Pplus space) and width covering the width of Poly2.
- logger.info("Executing rule HRES.12a")
- mk_hres12a = res_mk.edges.not(poly2.not(pplus).and(sab).edges).inside_part(poly2)
- hres12a_l1 = res_mk.interacting(resistor).interacting(mk_hres12a)
- hres12a_l1.output("HRES.12a", "HRES.12a : P type Poly2 resistor (high sheet rho) shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by Pplus space) and width covering the width of Poly2. ")
- hres12a_l1.forget
- mk_hres12a.forget
-
- # Rule HRES.12b: If the size of single RES_MK mark layer is greater than 15000 um2 and both side (X and Y) are greater than 80 um. Then the minimum spacing to adjacent RES_MK layer. is 20µm
- logger.info("Executing rule HRES.12b")
- hres12b = res_mk.with_area(15000.001.um,nil).edges.with_length(80.001.um,nil)
- hres12b_l1 = hres12b.separation(res_mk.edges, 20.um)
- hres12b_l1.output("HRES.12b", "HRES.12b : If the size of single RES_MK mark layer is greater than 15000 um2 and both side (X and Y) are greater than 80 um. Then the minimum spacing to adjacent RES_MK layer. : 20µm")
- hres12b_l1.forget
- hres12b.forget
- hres_poly.forget
-
-end #FEOL
-
diff --git a/klayout/drc/rule_decks/lres.drc b/klayout/drc/rule_decks/lres.drc
deleted file mode 100644
index 9f5d7ecd..00000000
--- a/klayout/drc/rule_decks/lres.drc
+++ /dev/null
@@ -1,100 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-
-if FEOL
- #================================================
- #----------------N+ POLY RESISTOR----------------
- #================================================
-
- logger.info("Starting LRES derivations")
- lres_poly = poly2.and(nplus).interacting(sab).interacting(res_mk)
-
- # Rule LRES.1: Minimum width of Poly2 resistor. is 0.8µm
- logger.info("Executing rule LRES.1")
- lres1_l1 = lres_poly.width(0.8.um, euclidian)
- lres1_l1.output("LRES.1", "LRES.1 : Minimum width of Poly2 resistor. : 0.8µm")
- lres1_l1.forget
-
- # Rule LRES.2: Minimum space between Poly2 resistors. is 0.4µm
- logger.info("Executing rule LRES.2")
- lres2_l1 = lres_poly.isolated(0.4.um, euclidian)
- lres2_l1.output("LRES.2", "LRES.2 : Minimum space between Poly2 resistors. : 0.4µm")
- lres2_l1.forget
-
- # Rule LRES.3: Minimum space from Poly2 resistor to COMP.
- logger.info("Executing rule LRES.3")
- lres3_l1 = lres_poly.separation(comp, 0.6.um, euclidian).polygons(0.001).or(comp.not_outside(lres_poly))
- lres3_l1.output("LRES.3", "LRES.3 : Minimum space from Poly2 resistor to COMP.")
- lres3_l1.forget
-
- # Rule LRES.4: Minimum space from Poly2 resistor to unrelated Poly2. is 0.6µm
- logger.info("Executing rule LRES.4")
- lres4_l1 = lres_poly.separation(poly2.not_interacting(sab), 0.6.um, euclidian)
- lres4_l1.output("LRES.4", "LRES.4 : Minimum space from Poly2 resistor to unrelated Poly2. : 0.6µm")
- lres4_l1.forget
-
- # Rule LRES.5: Minimum Nplus implant overlap of Poly2 resistor. is 0.3µm
- logger.info("Executing rule LRES.5")
- lres5_l1 = lres_poly.enclosed(nplus, 0.3.um, euclidian).polygons(0.001)
- lres5_l2 = lres_poly.not_outside(nplus).not(nplus)
- lres5_l = lres5_l1.or(lres5_l2)
- lres5_l.output("LRES.5", "LRES.5 : Minimum Nplus implant overlap of Poly2 resistor. : 0.3µm")
- lres5_l1.forget
- lres5_l2.forget
- lres5_l.forget
-
- # Rule LRES.6: Minimum salicide block overlap of Poly2 resistor in width direction. is 0.28µm
- logger.info("Executing rule LRES.6")
- lres6_l1 = lres_poly.enclosed(sab,0.28.um)
- lres6_l1.output("LRES.6", "LRES.6 : Minimum salicide block overlap of Poly2 resistor in width direction. : 0.28µm")
- lres6_l1.forget
-
- # Rule LRES.7: Space from salicide block to contact on Poly2 resistor.
- logger.info("Executing rule LRES.7")
- cont_lres7 = contact.and(lres_poly)
- lres7_l1 = cont_lres7.separation(sab,0.22.um).polygons(0.001)
- lres7_l2 = cont_lres7.interacting(sab)
- lres7_l = lres7_l1.or(lres7_l2)
- lres7_l.output("LRES.7", "LRES.7 : Space from salicide block to contact on Poly2 resistor.")
- lres7_l.forget
- lres7_l1.forget
- lres7_l2.forget
- cont_lres7.forget
-
- # Rule LRES.8 is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_02.html#n-poly-resistor-low-sheet-rho
-
- # Rule LRES.9a: Nplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2.
- logger.info("Executing rule LRES.9a")
- mk_lres9 = res_mk.edges.not(poly2.and(nplus).and(sab).edges).inside_part(poly2)
- lres9a_l1 = res_mk.interacting(lres_poly).interacting(mk_lres9)
- lres9a_l1.output("LRES.9a", "LRES.9a : Nplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2. ")
- lres9a_l1.forget
- mk_lres9.forget
-
- # Rule LRES.9b: If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um.
- ## then the minimum spacing to adjacent RES_MK layer. is 20µm
- logger.info("Executing rule LRES.9b")
- lres9b = res_mk.with_area(15000.001.um,nil).edges.with_length(80.001.um,nil)
- lres9b_l1 = lres9b.separation(res_mk.edges, 20.um)
- lres9b_l1.output("LRES.9b", "LRES.9b : If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um. then the minimum spacing to adjacent RES_MK layer. : 20µm")
- lres9b_l1.forget
- lres9b.forget
- lres_poly.forget
-
-end #FEOL
-
diff --git a/klayout/drc/rule_decks/lvs_bjt.drc b/klayout/drc/rule_decks/lvs_bjt.drc
deleted file mode 100644
index 38ad912b..00000000
--- a/klayout/drc/rule_decks/lvs_bjt.drc
+++ /dev/null
@@ -1,34 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-if FEOL
- #================================================
- #--------------------LVS_BJT---------------------
- #================================================
-
- vnpn_e = ncomp.interacting(lvs_bjt).inside(dnwell)
- vpnp_e = pcomp.inside(nwell).interacting(lvs_bjt)
- # Rule LVS_BJT.1: Minimum LVS_BJT enclosure of NPN or PNP Emitter COMP layers
- logger.info("Executing rule LVS_BJT.1")
- lvs_l1 = vnpn_e.or(vpnp_e).not_inside(lvs_bjt)
- lvs_l1.output("LVS_BJT.1", "LVS_BJT.1 : Minimum LVS_BJT enclosure of NPN or PNP Emitter COMP layers")
- lvs_l1.forget
-
- vnpn_e.forget
-
- vpnp_e.forget
-end #FEOL
-
diff --git a/klayout/drc/rule_decks/main.drc b/klayout/drc/rule_decks/main.drc
index e575e6c1..56a3b256 100644
--- a/klayout/drc/rule_decks/main.drc
+++ b/klayout/drc/rule_decks/main.drc
@@ -14,880 +14,788 @@
# limitations under the License.
################################################################################################
-#===========================================================================================================================
-#------------------------------------------- GF 0.18um MCU DRC RULE DECK --------------------------------------------------
-#===========================================================================================================================
+#=======================================================================================================================
+#--------------------------------------------- GF 0.18um MCU DRC RULE DECK ---------------------------------------------
+#=======================================================================================================================
require 'time'
-require "logger"
+require 'logger'
require 'etc'
exec_start_time = Time.now
-logger = Logger.new(STDOUT)
+logger = Logger.new($stdout)
-logger.formatter = proc do |severity, datetime, progname, msg|
- "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+logger.formatter = proc do |_severity, datetime, _progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10, 40].strip + ") : #{msg}
"
end
#================================================
#----------------- FILE SETUP -------------------
#================================================
-logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
-logger.info("Ruby Version for klayout: %s" % [RUBY_VERSION])
+logger.info("Starting running GF180MCU Klayout DRC runset on #{$input}")
+logger.info("Ruby Version for klayout: #{RUBY_VERSION}")
if $input
- if $topcell
- source($input, $topcell)
- else
- source($input)
- end
-end
-
-if $table_name
- table_name = $table_name
-else
- table_name = "main"
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
end
-
-logger.info("Loading database to memory is complete.")
+logger.info('Loading database to memory is complete.')
if $report
- logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
- report("DRC Run Report at", $report)
+ logger.info("GF180MCU Klayout DRC runset output at: #{$report}")
+ report('DRC Run Report at', $report)
else
- layout_dir = Pathname.new(RBA::CellView::active.filename).parent.realpath
- report_path = layout_dir.join("gf180_drc.lyrdb").to_s
- logger.info("GF180MCU Klayout DRC runset output at default location: %s" % report_path)
- report("DRC Run Report at", report_path)
+ layout_dir = Pathname.new(RBA::CellView.active.filename).parent.realpath
+ report_path = layout_dir.join('gf180_drc.lyrdb').to_s
+ logger.info('GF180MCU Klayout DRC runset output at default location: %s', report_path)
+ report('DRC Run Report at', report_path)
end
#================================================
#------------------ SWITCHES --------------------
#================================================
-logger.info("Evaluate switches.")
+logger.info('Evaluate switches.')
# connectivity rules
-if $conn_drc == "true"
+if $conn_drc == 'true'
CONNECTIVITY_RULES = $conn_drc
- logger.info("connectivity rules are enabled.")
+ logger.info('connectivity rules are enabled.')
else
CONNECTIVITY_RULES = false
- logger.info("connectivity rules are disabled.")
-end # connectivity rules
+ logger.info('connectivity rules are disabled.')
+end
# WEDGE
-if $wedge == "false"
- WEDGE = $wedge
-else
- WEDGE = "true"
-end # WEDGE
+WEDGE = if $wedge == 'false'
+ $wedge
+ else
+ 'true'
+ end
-logger.info("Wedge enabled %s" % [WEDGE])
+logger.info("Wedge enabled: #{WEDGE}")
# BALL
-if $ball == "false"
- BALL = $ball
-else
- BALL = "true"
-end # BALL
+BALL = if $ball == 'false'
+ $ball
+ else
+ 'true'
+ end
-logger.info("Ball enabled %s" % [BALL])
+logger.info("Ball enabled: #{BALL}")
# GOLD
-if $gold == "false"
- GOLD = $gold
-else
- GOLD = "true"
-end # GOLD
+GOLD = if $gold == 'false'
+ $gold
+ else
+ 'true'
+ end
-logger.info("Gold enabled %s" % [GOLD])
+logger.info("Gold enabled: #{GOLD}")
-if $mim_option
- MIM_OPTION = $mim_option
-else
- MIM_OPTION = "B"
-end
+# MIM
+MIM_OPTION = $mim_option || 'B'
-logger.info("MIM Option selected %s" % [MIM_OPTION])
+logger.info("MIM Option selected: #{MIM_OPTION}")
# OFFGRID
-if $offgrid == "false"
- OFFGRID = false
-else
- OFFGRID = true
-end # OFFGRID
+OFFGRID = $offgrid != 'false'
-logger.info("Offgrid enabled %s" % [OFFGRID])
+logger.info("Offgrid enabled: #{OFFGRID}")
+# threads
if $thr
- threads($thr)
+ threads($thr)
else
- thr ||= Etc.nprocessors
- threads(thr)
+ thr ||= Etc.nprocessors
+ threads(thr)
end
-logger.info("Number of threads to use %s" % [$thr])
+
+logger.info("Number of threads to use #{$thr}")
#=== PRINT DETAILS ===
logger.info("Verbose mode: #{$verbose}")
-if $verbose == "true"
+if $verbose == 'true'
verbose(true)
else
verbose(false)
end
# === TILING MODE ===
-if $run_mode == "tiling"
+case $run_mode
+when 'tiling'
tiles(500.um)
tile_borders(10.um)
- logger.info("Tiling mode is enabled.")
+ logger.info('Tiling mode is enabled.')
-elsif $run_mode == "deep"
+when 'deep'
#=== HIER MODE ===
deep
- logger.info("deep mode is enabled.")
+ logger.info('deep mode is enabled.')
else
#=== FLAT MODE ===
flat
- logger.info("flat mode is enabled.")
-end # run_mode
+ logger.info('flat mode is enabled.')
+end
# METAL_TOP
-if $metal_top
- METAL_TOP = $metal_top
-else
- METAL_TOP = "9K"
-end # METAL_TOP
+METAL_TOP = $metal_top || '9K'
-logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+logger.info("METAL_TOP Selected is #{METAL_TOP}")
# METAL_LEVEL
-if $metal_level
- METAL_LEVEL = $metal_level
-else
- METAL_LEVEL = "5LM"
-end # METAL_LEVEL
+METAL_LEVEL = $metal_level || '5LM'
-logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+logger.info("METAL_STACK Selected is #{METAL_LEVEL}")
# FEOL
-if $feol == "false"
+if $feol == 'false'
FEOL = $feol
- logger.info("FEOL is disabled.")
+ logger.info('FEOL is disabled.')
else
- FEOL = "true"
- logger.info("FEOL is enabled.")
-end # FEOL
+ FEOL = 'true'.freeze
+ logger.info('FEOL is enabled.')
+end
# BEOL
-if $beol == "false"
+if $beol == 'false'
BEOL = $beol
- logger.info("BEOL is disabled.")
+ logger.info('BEOL is disabled.')
else
- BEOL = "true"
- logger.info("BEOL is enabled.")
-end # BEOL
+ BEOL = 'true'.freeze
+ logger.info('BEOL is enabled.')
+end
#================================================
#------------- LAYERS DEFINITIONS ---------------
#================================================
polygons_count = 0
-logger.info("Read in polygons from layers.")
+logger.info('Read in polygons from layers.')
-def get_polygons(l, d)
- ps = polygons(l, d)
- return $run_mode == "deep" ? ps : ps.merged
+def get_polygons(layer, data_type)
+ ps = polygons(layer, data_type)
+ $run_mode == 'deep' ? ps : ps.merged
end
-comp = get_polygons(22 , 0 )
-count = comp.count()
-logger.info("comp has %d polygons" % [count])
-polygons_count += count
-
-dnwell = get_polygons(12 , 0 )
-count = dnwell.count()
-logger.info("dnwell has %d polygons" % [count])
-polygons_count += count
-
-nwell = get_polygons(21 , 0 )
-count = nwell.count()
-logger.info("nwell has %d polygons" % [count])
-polygons_count += count
-
-lvpwell = get_polygons(204, 0 )
-count = lvpwell.count()
-logger.info("lvpwell has %d polygons" % [count])
-polygons_count += count
-
-dualgate = get_polygons(55 , 0 )
-count = dualgate.count()
-logger.info("dualgate has %d polygons" % [count])
-polygons_count += count
-
-poly2 = get_polygons(30 , 0 )
-count = poly2.count()
-logger.info("poly2 has %d polygons" % [count])
-polygons_count += count
-
-nplus = get_polygons(32 , 0 )
-count = nplus.count()
-logger.info("nplus has %d polygons" % [count])
-polygons_count += count
-
-pplus = get_polygons(31 , 0 )
-count = pplus.count()
-logger.info("pplus has %d polygons" % [count])
-polygons_count += count
-
-sab = get_polygons(49 , 0 )
-count = sab.count()
-logger.info("sab has %d polygons" % [count])
-polygons_count += count
-
-esd = get_polygons(24 , 0 )
-count = esd.count()
-logger.info("esd has %d polygons" % [count])
-polygons_count += count
-
-resistor = get_polygons(62 , 0 )
-count = resistor.count()
-logger.info("resistor has %d polygons" % [count])
-polygons_count += count
-
-fhres = get_polygons(227, 0 )
-count = fhres.count()
-logger.info("fhres has %d polygons" % [count])
-polygons_count += count
-
-fusetop = get_polygons(75 , 0 )
-count = fusetop.count()
-logger.info("fusetop has %d polygons" % [count])
-polygons_count += count
-
-fusewindow_d = get_polygons(96 , 1 )
-count = fusewindow_d.count()
-logger.info("fusewindow_d has %d polygons" % [count])
-polygons_count += count
-
-polyfuse = get_polygons(220, 0 )
-count = polyfuse.count()
-logger.info("polyfuse has %d polygons" % [count])
-polygons_count += count
-
-mvsd = get_polygons(210, 0 )
-count = mvsd.count()
-logger.info("mvsd has %d polygons" % [count])
-polygons_count += count
-
-mvpsd = get_polygons(11 , 39)
-count = mvpsd.count()
-logger.info("mvpsd has %d polygons" % [count])
-polygons_count += count
-
-nat = get_polygons(5 , 0 )
-count = nat.count()
-logger.info("nat has %d polygons" % [count])
-polygons_count += count
-
-comp_dummy = get_polygons(22 , 4 )
-count = comp_dummy.count()
-logger.info("comp_dummy has %d polygons" % [count])
-polygons_count += count
-
-poly2_dummy = get_polygons(30 , 4 )
-count = poly2_dummy.count()
-logger.info("poly2_dummy has %d polygons" % [count])
-polygons_count += count
-
-schottky_diode = get_polygons(241, 0 )
-count = schottky_diode.count()
-logger.info("schottky_diode has %d polygons" % [count])
-polygons_count += count
-
-zener = get_polygons(178, 0 )
-count = zener.count()
-logger.info("zener has %d polygons" % [count])
-polygons_count += count
-
-res_mk = get_polygons(110, 5 )
-count = res_mk.count()
-logger.info("res_mk has %d polygons" % [count])
-polygons_count += count
-
-opc_drc = get_polygons(124, 5 )
-count = opc_drc.count()
-logger.info("opc_drc has %d polygons" % [count])
-polygons_count += count
-
-ndmy = get_polygons(111, 5 )
-count = ndmy.count()
-logger.info("ndmy has %d polygons" % [count])
-polygons_count += count
-
-pmndmy = get_polygons(152, 5 )
-count = pmndmy.count()
-logger.info("pmndmy has %d polygons" % [count])
-polygons_count += count
-
-v5_xtor = get_polygons(112, 1 )
-count = v5_xtor.count()
-logger.info("v5_xtor has %d polygons" % [count])
-polygons_count += count
-
-cap_mk = get_polygons(117, 5 )
-count = cap_mk.count()
-logger.info("cap_mk has %d polygons" % [count])
-polygons_count += count
-
-mos_cap_mk = get_polygons(166, 5 )
-count = mos_cap_mk.count()
-logger.info("mos_cap_mk has %d polygons" % [count])
-polygons_count += count
-
-ind_mk = get_polygons(151, 5 )
-count = ind_mk.count()
-logger.info("ind_mk has %d polygons" % [count])
-polygons_count += count
-
-diode_mk = get_polygons(115, 5 )
-count = diode_mk.count()
-logger.info("diode_mk has %d polygons" % [count])
-polygons_count += count
-
-drc_bjt = get_polygons(127, 5 )
-count = drc_bjt.count()
-logger.info("drc_bjt has %d polygons" % [count])
-polygons_count += count
-
-lvs_bjt = get_polygons(118, 5 )
-count = lvs_bjt.count()
-logger.info("lvs_bjt has %d polygons" % [count])
-polygons_count += count
-
-mim_l_mk = get_polygons(117, 10)
-count = mim_l_mk.count()
-logger.info("mim_l_mk has %d polygons" % [count])
-polygons_count += count
-
-latchup_mk = get_polygons(137, 5 )
-count = latchup_mk.count()
-logger.info("latchup_mk has %d polygons" % [count])
-polygons_count += count
-
-guard_ring_mk = get_polygons(167, 5 )
-count = guard_ring_mk.count()
-logger.info("guard_ring_mk has %d polygons" % [count])
-polygons_count += count
-
-otp_mk = get_polygons(173, 5 )
-count = otp_mk.count()
-logger.info("otp_mk has %d polygons" % [count])
-polygons_count += count
-
-mtpmark = get_polygons(122, 5 )
-count = mtpmark.count()
-logger.info("mtpmark has %d polygons" % [count])
-polygons_count += count
-
-neo_ee_mk = get_polygons(88 , 17)
-count = neo_ee_mk.count()
-logger.info("neo_ee_mk has %d polygons" % [count])
-polygons_count += count
-
-sramcore = get_polygons(108, 5 )
-count = sramcore.count()
-logger.info("sramcore has %d polygons" % [count])
-polygons_count += count
-
-lvs_rf = get_polygons(100, 5 )
-count = lvs_rf.count()
-logger.info("lvs_rf has %d polygons" % [count])
-polygons_count += count
-
-lvs_drain = get_polygons(100, 7 )
-count = lvs_drain.count()
-logger.info("lvs_drain has %d polygons" % [count])
-polygons_count += count
-
-ind_mk = get_polygons(151, 5 )
-count = ind_mk.count()
-logger.info("ind_mk has %d polygons" % [count])
-polygons_count += count
-
-hvpolyrs = get_polygons(123, 5 )
-count = hvpolyrs.count()
-logger.info("hvpolyrs has %d polygons" % [count])
-polygons_count += count
-
-lvs_io = get_polygons(119, 5 )
-count = lvs_io.count()
-logger.info("lvs_io has %d polygons" % [count])
-polygons_count += count
-
-probe_mk = get_polygons(13 , 17)
-count = probe_mk.count()
-logger.info("probe_mk has %d polygons" % [count])
-polygons_count += count
-
-esd_mk = get_polygons(24 , 5 )
-count = esd_mk.count()
-logger.info("esd_mk has %d polygons" % [count])
-polygons_count += count
-
-lvs_source = get_polygons(100, 8 )
-count = lvs_source.count()
-logger.info("lvs_source has %d polygons" % [count])
-polygons_count += count
-
-well_diode_mk = get_polygons(153, 51)
-count = well_diode_mk.count()
-logger.info("well_diode_mk has %d polygons" % [count])
-polygons_count += count
-
-ldmos_xtor = get_polygons(226, 0 )
-count = ldmos_xtor.count()
-logger.info("ldmos_xtor has %d polygons" % [count])
-polygons_count += count
-
-plfuse = get_polygons(125, 5 )
-count = plfuse.count()
-logger.info("plfuse has %d polygons" % [count])
-polygons_count += count
-
-efuse_mk = get_polygons(80 , 5 )
-count = efuse_mk.count()
-logger.info("efuse_mk has %d polygons" % [count])
-polygons_count += count
-
-mcell_feol_mk = get_polygons(11 , 17)
-count = mcell_feol_mk.count()
-logger.info("mcell_feol_mk has %d polygons" % [count])
-polygons_count += count
-
-ymtp_mk = get_polygons(86 , 17)
-count = ymtp_mk.count()
-logger.info("ymtp_mk has %d polygons" % [count])
-polygons_count += count
-
-dev_wf_mk = get_polygons(128, 17)
-count = dev_wf_mk.count()
-logger.info("dev_wf_mk has %d polygons" % [count])
-polygons_count += count
-
-comp_label = get_polygons(22 , 10)
-count = comp_label.count()
-logger.info("comp_label has %d polygons" % [count])
-polygons_count += count
-
-poly2_label = get_polygons(30 , 10)
-count = poly2_label.count()
-logger.info("poly2_label has %d polygons" % [count])
-polygons_count += count
-
-mdiode = get_polygons(116, 5 )
-count = mdiode.count()
-logger.info("mdiode has %d polygons" % [count])
-polygons_count += count
-
-contact = get_polygons(33 , 0 )
-count = contact.count()
-logger.info("contact has %d polygons" % [count])
-polygons_count += count
-
-metal1_drawn = get_polygons(34 , 0 )
-count = metal1_drawn.count()
-logger.info("metal1_drawn has %d polygons" % [count])
-polygons_count += count
-
-metal1_dummy = get_polygons(34 , 4 )
-count = metal1_dummy.count()
-logger.info("metal1_dummy has %d polygons" % [count])
-polygons_count += count
+comp = get_polygons(22, 0)
+count = comp.count
+logger.info("comp has #{count} polygons")
+polygons_count += count
+
+dnwell = get_polygons(12, 0)
+count = dnwell.count
+logger.info("dnwell has #{count} polygons")
+polygons_count += count
+
+nwell = get_polygons(21, 0)
+count = nwell.count
+logger.info("nwell has #{count} polygons")
+polygons_count += count
+
+lvpwell = get_polygons(204, 0)
+count = lvpwell.count
+logger.info("lvpwell has #{count} polygons")
+polygons_count += count
+
+dualgate = get_polygons(55, 0)
+count = dualgate.count
+logger.info("dualgate has #{count} polygons")
+polygons_count += count
+
+poly2 = get_polygons(30, 0)
+count = poly2.count
+logger.info("poly2 has #{count} polygons")
+polygons_count += count
+
+nplus = get_polygons(32, 0)
+count = nplus.count
+logger.info("nplus has #{count} polygons")
+polygons_count += count
+
+pplus = get_polygons(31, 0)
+count = pplus.count
+logger.info("pplus has #{count} polygons")
+polygons_count += count
+
+sab = get_polygons(49, 0)
+count = sab.count
+logger.info("sab has #{count} polygons")
+polygons_count += count
+
+esd = get_polygons(24, 0)
+count = esd.count
+logger.info("esd has #{count} polygons")
+polygons_count += count
+
+resistor = get_polygons(62, 0)
+count = resistor.count
+logger.info("resistor has #{count} polygons")
+polygons_count += count
+
+fhres = get_polygons(227, 0)
+count = fhres.count
+logger.info("fhres has #{count} polygons")
+polygons_count += count
+
+fusetop = get_polygons(75, 0)
+count = fusetop.count
+logger.info("fusetop has #{count} polygons")
+polygons_count += count
+
+fusewindow_d = get_polygons(96, 1)
+count = fusewindow_d.count
+logger.info("fusewindow_d has #{count} polygons")
+polygons_count += count
+
+polyfuse = get_polygons(220, 0)
+count = polyfuse.count
+logger.info("polyfuse has #{count} polygons")
+polygons_count += count
+
+mvsd = get_polygons(210, 0)
+count = mvsd.count
+logger.info("mvsd has #{count} polygons")
+polygons_count += count
+
+mvpsd = get_polygons(11, 39)
+count = mvpsd.count
+logger.info("mvpsd has #{count} polygons")
+polygons_count += count
+
+nat = get_polygons(5, 0)
+count = nat.count
+logger.info("nat has #{count} polygons")
+polygons_count += count
+
+comp_dummy = get_polygons(22, 4)
+count = comp_dummy.count
+logger.info("comp_dummy has #{count} polygons")
+polygons_count += count
+
+poly2_dummy = get_polygons(30, 4)
+count = poly2_dummy.count
+logger.info("poly2_dummy has #{count} polygons")
+polygons_count += count
+
+schottky_diode = get_polygons(241, 0)
+count = schottky_diode.count
+logger.info("schottky_diode has #{count} polygons")
+polygons_count += count
+
+zener = get_polygons(178, 0)
+count = zener.count
+logger.info("zener has #{count} polygons")
+polygons_count += count
+
+res_mk = get_polygons(110, 5)
+count = res_mk.count
+logger.info("res_mk has #{count} polygons")
+polygons_count += count
+
+opc_drc = get_polygons(124, 5)
+count = opc_drc.count
+logger.info("opc_drc has #{count} polygons")
+polygons_count += count
+
+ndmy = get_polygons(111, 5)
+count = ndmy.count
+logger.info("ndmy has #{count} polygons")
+polygons_count += count
+
+pmndmy = get_polygons(152, 5)
+count = pmndmy.count
+logger.info("pmndmy has #{count} polygons")
+polygons_count += count
+
+v5_xtor = get_polygons(112, 1)
+count = v5_xtor.count
+logger.info("v5_xtor has #{count} polygons")
+polygons_count += count
+
+cap_mk = get_polygons(117, 5)
+count = cap_mk.count
+logger.info("cap_mk has #{count} polygons")
+polygons_count += count
+
+mos_cap_mk = get_polygons(166, 5)
+count = mos_cap_mk.count
+logger.info("mos_cap_mk has #{count} polygons")
+polygons_count += count
+
+ind_mk = get_polygons(151, 5)
+count = ind_mk.count
+logger.info("ind_mk has #{count} polygons")
+polygons_count += count
+
+diode_mk = get_polygons(115, 5)
+count = diode_mk.count
+logger.info("diode_mk has #{count} polygons")
+polygons_count += count
+
+drc_bjt = get_polygons(127, 5)
+count = drc_bjt.count
+logger.info("drc_bjt has #{count} polygons")
+polygons_count += count
+
+lvs_bjt = get_polygons(118, 5)
+count = lvs_bjt.count
+logger.info("lvs_bjt has #{count} polygons")
+polygons_count += count
+
+mim_l_mk = get_polygons(117, 10)
+count = mim_l_mk.count
+logger.info("mim_l_mk has #{count} polygons")
+polygons_count += count
+
+latchup_mk = get_polygons(137, 5)
+count = latchup_mk.count
+logger.info("latchup_mk has #{count} polygons")
+polygons_count += count
+
+guard_ring_mk = get_polygons(167, 5)
+count = guard_ring_mk.count
+logger.info("guard_ring_mk has #{count} polygons")
+polygons_count += count
+
+otp_mk = get_polygons(173, 5)
+count = otp_mk.count
+logger.info("otp_mk has #{count} polygons")
+polygons_count += count
+
+mtpmark = get_polygons(122, 5)
+count = mtpmark.count
+logger.info("mtpmark has #{count} polygons")
+polygons_count += count
+
+neo_ee_mk = get_polygons(88, 17)
+count = neo_ee_mk.count
+logger.info("neo_ee_mk has #{count} polygons")
+polygons_count += count
+
+sramcore = get_polygons(108, 5)
+count = sramcore.count
+logger.info("sramcore has #{count} polygons")
+polygons_count += count
+
+lvs_rf = get_polygons(100, 5)
+count = lvs_rf.count
+logger.info("lvs_rf has #{count} polygons")
+polygons_count += count
+
+lvs_drain = get_polygons(100, 7)
+count = lvs_drain.count
+logger.info("lvs_drain has #{count} polygons")
+polygons_count += count
+
+ind_mk = get_polygons(151, 5)
+count = ind_mk.count
+logger.info("ind_mk has #{count} polygons")
+polygons_count += count
+
+hvpolyrs = get_polygons(123, 5)
+count = hvpolyrs.count
+logger.info("hvpolyrs has #{count} polygons")
+polygons_count += count
+
+lvs_io = get_polygons(119, 5)
+count = lvs_io.count
+logger.info("lvs_io has #{count} polygons")
+polygons_count += count
+
+probe_mk = get_polygons(13, 17)
+count = probe_mk.count
+logger.info("probe_mk has #{count} polygons")
+polygons_count += count
+
+esd_mk = get_polygons(24, 5)
+count = esd_mk.count
+logger.info("esd_mk has #{count} polygons")
+polygons_count += count
+
+lvs_source = get_polygons(100, 8)
+count = lvs_source.count
+logger.info("lvs_source has #{count} polygons")
+polygons_count += count
+
+well_diode_mk = get_polygons(153, 51)
+count = well_diode_mk.count
+logger.info("well_diode_mk has #{count} polygons")
+polygons_count += count
+
+ldmos_xtor = get_polygons(226, 0)
+count = ldmos_xtor.count
+logger.info("ldmos_xtor has #{count} polygons")
+polygons_count += count
+
+plfuse = get_polygons(125, 5)
+count = plfuse.count
+logger.info("plfuse has #{count} polygons")
+polygons_count += count
+
+efuse_mk = get_polygons(80, 5)
+count = efuse_mk.count
+logger.info("efuse_mk has #{count} polygons")
+polygons_count += count
+
+mcell_feol_mk = get_polygons(11, 17)
+count = mcell_feol_mk.count
+logger.info("mcell_feol_mk has #{count} polygons")
+polygons_count += count
+
+ymtp_mk = get_polygons(86, 17)
+count = ymtp_mk.count
+logger.info("ymtp_mk has #{count} polygons")
+polygons_count += count
+
+dev_wf_mk = get_polygons(128, 17)
+count = dev_wf_mk.count
+logger.info("dev_wf_mk has #{count} polygons")
+polygons_count += count
+
+comp_label = get_polygons(22, 10)
+count = comp_label.count
+logger.info("comp_label has #{count} polygons")
+polygons_count += count
+
+poly2_label = get_polygons(30, 10)
+count = poly2_label.count
+logger.info("poly2_label has #{count} polygons")
+polygons_count += count
+
+mdiode = get_polygons(116, 5)
+count = mdiode.count
+logger.info("mdiode has #{count} polygons")
+polygons_count += count
+
+contact = get_polygons(33, 0)
+count = contact.count
+logger.info("contact has #{count} polygons")
+polygons_count += count
+
+metal1_drawn = get_polygons(34, 0)
+count = metal1_drawn.count
+logger.info("metal1_drawn has #{count} polygons")
+polygons_count += count
+
+metal1_dummy = get_polygons(34, 4)
+count = metal1_dummy.count
+logger.info("metal1_dummy has #{count} polygons")
+polygons_count += count
metal1 = metal1_drawn + metal1_dummy
-metal1_label = get_polygons(34 , 10)
-count = metal1_label.count()
-logger.info("metal1_label has %d polygons" % [count])
-polygons_count += count
-
-metal1_slot = get_polygons(34 , 3 )
-count = metal1_slot.count()
-logger.info("metal1_slot has %d polygons" % [count])
-polygons_count += count
-
-metal1_blk = get_polygons(34 , 5 )
-count = metal1_blk.count()
-logger.info("metal1_blk has %d polygons" % [count])
-polygons_count += count
-
-via1 = get_polygons(35 , 0 )
-count = via1.count()
-logger.info("via1 has %d polygons" % [count])
-polygons_count += count
-
-
-if METAL_LEVEL == "2LM"
- metal2_drawn = get_polygons(36 , 0 )
- count = metal2_drawn.count()
- logger.info("metal2_drawn has %d polygons" % [count])
- polygons_count += count
-
- metal2_dummy = get_polygons(36 , 4 )
- count = metal2_dummy.count()
- logger.info("metal2_dummy has %d polygons" % [count])
- polygons_count += count
-
- metal2 = metal2_drawn + metal2_dummy
-
- metal2_label = get_polygons(36 , 10)
- count = metal2_label.count()
- logger.info("metal2_label has %d polygons" % [count])
- polygons_count += count
-
- metal2_slot = get_polygons(36 , 3 )
- count = metal2_slot.count()
- logger.info("metal2_slot has %d polygons" % [count])
- polygons_count += count
-
- metal2_blk = get_polygons(36 , 5 )
- count = metal2_blk.count()
- logger.info("metal2_blk has %d polygons" % [count])
- polygons_count += count
-
+metal1_label = get_polygons(34, 10)
+count = metal1_label.count
+logger.info("metal1_label has #{count} polygons")
+polygons_count += count
+
+metal1_slot = get_polygons(34, 3)
+count = metal1_slot.count
+logger.info("metal1_slot has #{count} polygons")
+polygons_count += count
+
+metal1_blk = get_polygons(34, 5)
+count = metal1_blk.count
+logger.info("metal1_blk has #{count} polygons")
+polygons_count += count
+
+via1 = get_polygons(35, 0)
+count = via1.count
+logger.info("via1 has #{count} polygons")
+polygons_count += count
+
+metal2_drawn = get_polygons(36, 0)
+count = metal2_drawn.count
+logger.info("metal2_drawn has #{count} polygons")
+polygons_count += count
+
+metal2_dummy = get_polygons(36, 4)
+count = metal2_dummy.count
+logger.info("metal2_dummy has #{count} polygons")
+polygons_count += count
+
+metal2 = metal2_drawn + metal2_dummy
+
+metal2_label = get_polygons(36, 10)
+count = metal2_label.count
+logger.info("metal2_label has #{count} polygons")
+polygons_count += count
+
+metal2_slot = get_polygons(36, 3)
+count = metal2_slot.count
+logger.info("metal2_slot has #{count} polygons")
+polygons_count += count
+
+metal2_blk = get_polygons(36, 5)
+count = metal2_blk.count
+logger.info("metal2_blk has #{count} polygons")
+polygons_count += count
+
+if METAL_LEVEL == '2LM'
+
top_via = via1
topmin1_via = contact
top_metal = metal2
topmin1_metal = metal1
-
+
else
- metal2_drawn = get_polygons(36 , 0 )
- count = metal2_drawn.count()
- logger.info("metal2_drawn has %d polygons" % [count])
- polygons_count += count
-
- metal2_dummy = get_polygons(36 , 4 )
- count = metal2_dummy.count()
- logger.info("metal2_dummy has %d polygons" % [count])
- polygons_count += count
-
- metal2 = metal2_drawn + metal2_dummy
-
- metal2_label = get_polygons(36 , 10)
- count = metal2_label.count()
- logger.info("metal2_label has %d polygons" % [count])
- polygons_count += count
-
- metal2_slot = get_polygons(36 , 3 )
- count = metal2_slot.count()
- logger.info("metal2_slot has %d polygons" % [count])
- polygons_count += count
-
- metal2_blk = get_polygons(36 , 5 )
- count = metal2_blk.count()
- logger.info("metal2_blk has %d polygons" % [count])
- polygons_count += count
-
- via2 = get_polygons(38 , 0 )
- count = via2.count()
- logger.info("via2 has %d polygons" % [count])
- polygons_count += count
-
- if METAL_LEVEL == "3LM"
- metal3_drawn = get_polygons(42 , 0 )
- count = metal3_drawn.count()
- logger.info("metal3_drawn has %d polygons" % [count])
- polygons_count += count
-
- metal3_dummy = get_polygons(42 , 4 )
- count = metal3_dummy.count()
- logger.info("metal3_dummy has %d polygons" % [count])
- polygons_count += count
-
- metal3 = metal3_drawn + metal3_dummy
-
- metal3_label = get_polygons(42 , 10)
- count = metal3_label.count()
- logger.info("metal3_label has %d polygons" % [count])
- polygons_count += count
-
- metal3_slot = get_polygons(42 , 3 )
- count = metal3_slot.count()
- logger.info("metal3_slot has %d polygons" % [count])
- polygons_count += count
-
- metal3_blk = get_polygons(42 , 5 )
- count = metal3_blk.count()
- logger.info("metal3_blk has %d polygons" % [count])
- polygons_count += count
-
+
+ via2 = get_polygons(38, 0)
+ count = via2.count
+ logger.info("via2 has #{count} polygons")
+ polygons_count += count
+
+ metal3_drawn = get_polygons(42, 0)
+ count = metal3_drawn.count
+ logger.info("metal3_drawn has #{count} polygons")
+ polygons_count += count
+
+ metal3_dummy = get_polygons(42, 4)
+ count = metal3_dummy.count
+ logger.info("metal3_dummy has #{count} polygons")
+ polygons_count += count
+
+ metal3 = metal3_drawn + metal3_dummy
+
+ metal3_label = get_polygons(42, 10)
+ count = metal3_label.count
+ logger.info("metal3_label has #{count} polygons")
+ polygons_count += count
+
+ metal3_slot = get_polygons(42, 3)
+ count = metal3_slot.count
+ logger.info("metal3_slot has #{count} polygons")
+ polygons_count += count
+
+ metal3_blk = get_polygons(42, 5)
+ count = metal3_blk.count
+ logger.info("metal3_blk has #{count} polygons")
+ polygons_count += count
+
+ if METAL_LEVEL == '3LM'
+
top_via = via2
topmin1_via = via1
top_metal = metal3
topmin1_metal = metal2
else
- metal3_drawn = get_polygons(42 , 0 )
- count = metal3_drawn.count()
- logger.info("metal3_drawn has %d polygons" % [count])
- polygons_count += count
-
- metal3_dummy = get_polygons(42 , 4 )
- count = metal3_dummy.count()
- logger.info("metal3_dummy has %d polygons" % [count])
- polygons_count += count
-
- metal3 = metal3_drawn + metal3_dummy
-
- metal3_label = get_polygons(42 , 10)
- count = metal3_label.count()
- logger.info("metal3_label has %d polygons" % [count])
- polygons_count += count
-
- metal3_slot = get_polygons(42 , 3 )
- count = metal3_slot.count()
- logger.info("metal3_slot has %d polygons" % [count])
- polygons_count += count
-
- metal3_blk = get_polygons(42 , 5 )
- count = metal3_blk.count()
- logger.info("metal3_blk has %d polygons" % [count])
- polygons_count += count
-
- via3 = get_polygons(40 , 0 )
-
- if METAL_LEVEL == "4LM"
- metal4_drawn = get_polygons(46 , 0 )
- count = metal4_drawn.count()
- logger.info("metal4_drawn has %d polygons" % [count])
- polygons_count += count
-
- metal4_dummy = get_polygons(46 , 4 )
- count = metal4_dummy.count()
- logger.info("metal4_dummy has %d polygons" % [count])
- polygons_count += count
-
- metal4 = metal4_drawn + metal4_dummy
-
- metal4_label = get_polygons(46 , 10)
- count = metal4_label.count()
- logger.info("metal4_label has %d polygons" % [count])
- polygons_count += count
-
- metal4_slot = get_polygons(46 , 3 )
- count = metal4_slot.count()
- logger.info("metal4_slot has %d polygons" % [count])
- polygons_count += count
-
- metal4_blk = get_polygons(46 , 5 )
- count = metal4_blk.count()
- logger.info("metal4_blk has %d polygons" % [count])
- polygons_count += count
-
+
+ via3 = get_polygons(40, 0)
+ count = via3.count
+ logger.info("via3 has #{count} polygons")
+ polygons_count += count
+
+ metal4_drawn = get_polygons(46, 0)
+ count = metal4_drawn.count
+ logger.info("metal4_drawn has #{count} polygons")
+ polygons_count += count
+
+ metal4_dummy = get_polygons(46, 4)
+ count = metal4_dummy.count
+ logger.info("metal4_dummy has #{count} polygons")
+ polygons_count += count
+
+ metal4 = metal4_drawn + metal4_dummy
+
+ metal4_label = get_polygons(46, 10)
+ count = metal4_label.count
+ logger.info("metal4_label has #{count} polygons")
+ polygons_count += count
+
+ metal4_slot = get_polygons(46, 3)
+ count = metal4_slot.count
+ logger.info("metal4_slot has #{count} polygons")
+ polygons_count += count
+
+ metal4_blk = get_polygons(46, 5)
+ count = metal4_blk.count
+ logger.info("metal4_blk has #{count} polygons")
+ polygons_count += count
+
+ if METAL_LEVEL == '4LM'
+
top_via = via3
topmin1_via = via2
top_metal = metal4
topmin1_metal = metal3
else
- metal4_drawn = get_polygons(46 , 0 )
- count = metal4_drawn.count()
- logger.info("metal4_drawn has %d polygons" % [count])
- polygons_count += count
-
- metal4_dummy = get_polygons(46 , 4 )
- count = metal4_dummy.count()
- logger.info("metal4_dummy has %d polygons" % [count])
- polygons_count += count
-
- metal4 = metal4_drawn + metal4_dummy
-
- metal4_label = get_polygons(46 , 10)
- count = metal4_label.count()
- logger.info("metal4_label has %d polygons" % [count])
- polygons_count += count
-
- metal4_slot = get_polygons(46 , 3 )
- count = metal4_slot.count()
- logger.info("metal4_slot has %d polygons" % [count])
- polygons_count += count
-
- metal4_blk = get_polygons(46 , 5 )
- count = metal4_blk.count()
- logger.info("metal4_blk has %d polygons" % [count])
- polygons_count += count
-
- via4 = get_polygons(41 , 0 )
- count = via4.count()
- logger.info("via4 has %d polygons" % [count])
- polygons_count += count
-
- if METAL_LEVEL == "5LM"
- metal5_drawn = get_polygons(81 , 0 )
- count = metal5_drawn.count()
- logger.info("metal5_drawn has %d polygons" % [count])
- polygons_count += count
-
- metal5_dummy = get_polygons(81 , 4 )
- count = metal5_dummy.count()
- logger.info("metal5_dummy has %d polygons" % [count])
- polygons_count += count
-
- metal5 = metal5_drawn + metal5_dummy
-
- metal5_label = get_polygons(81 , 10)
- count = metal5_label.count()
- logger.info("metal5_label has %d polygons" % [count])
- polygons_count += count
-
- metal5_slot = get_polygons(81 , 3 )
- count = metal5_slot.count()
- logger.info("metal5_slot has %d polygons" % [count])
- polygons_count += count
-
- metal5_blk = get_polygons(81 , 5 )
- count = metal5_blk.count()
- logger.info("metal5_blk has %d polygons" % [count])
- polygons_count += count
+
+ via4 = get_polygons(41, 0)
+ count = via4.count
+ logger.info("via4 has #{count} polygons")
+ polygons_count += count
+
+ case METAL_LEVEL
+ when '5LM'
+ metal5_drawn = get_polygons(81, 0)
+ count = metal5_drawn.count
+ logger.info("metal5_drawn has #{count} polygons")
+ polygons_count += count
+
+ metal5_dummy = get_polygons(81, 4)
+ count = metal5_dummy.count
+ logger.info("metal5_dummy has #{count} polygons")
+ polygons_count += count
+
+ metal5 = metal5_drawn + metal5_dummy
+
+ metal5_label = get_polygons(81, 10)
+ count = metal5_label.count
+ logger.info("metal5_label has #{count} polygons")
+ polygons_count += count
+
+ metal5_slot = get_polygons(81, 3)
+ count = metal5_slot.count
+ logger.info("metal5_slot has #{count} polygons")
+ polygons_count += count
+
+ metal5_blk = get_polygons(81, 5)
+ count = metal5_blk.count
+ logger.info("metal5_blk has #{count} polygons")
+ polygons_count += count
top_via = via4
topmin1_via = via3
top_metal = metal5
topmin1_metal = metal4
- elsif METAL_LEVEL == "6LM"
- metal5_drawn = get_polygons(81 , 0 )
- count = metal5_drawn.count()
- logger.info("metal5_drawn has %d polygons" % [count])
- polygons_count += count
+ when '6LM'
+ metal5_drawn = get_polygons(81, 0)
+ count = metal5_drawn.count
+ logger.info("metal5_drawn has #{count} polygons")
+ polygons_count += count
- metal5_dummy = get_polygons(81 , 4 )
- count = metal5_dummy.count()
- logger.info("metal5_dummy has %d polygons" % [count])
- polygons_count += count
+ metal5_dummy = get_polygons(81, 4)
+ count = metal5_dummy.count
+ logger.info("metal5_dummy has #{count} polygons")
+ polygons_count += count
metal5 = metal5_drawn + metal5_dummy
- metal5_label = get_polygons(81 , 10)
- count = metal5_label.count()
- logger.info("metal5_label has %d polygons" % [count])
- polygons_count += count
-
- metal5_slot = get_polygons(81 , 3 )
- count = metal5_slot.count()
- logger.info("metal5_slot has %d polygons" % [count])
- polygons_count += count
+ metal5_label = get_polygons(81, 10)
+ count = metal5_label.count
+ logger.info("metal5_label has #{count} polygons")
+ polygons_count += count
- metal5_blk = get_polygons(81 , 5 )
- count = metal5_blk.count()
- logger.info("metal5_blk has %d polygons" % [count])
- polygons_count += count
+ metal5_slot = get_polygons(81, 3)
+ count = metal5_slot.count
+ logger.info("metal5_slot has #{count} polygons")
+ polygons_count += count
- via5 = get_polygons(82 , 0 )
- count = via5.count()
- logger.info("via5 has %d polygons" % [count])
- polygons_count += count
+ metal5_blk = get_polygons(81, 5)
+ count = metal5_blk.count
+ logger.info("metal5_blk has #{count} polygons")
+ polygons_count += count
+ via5 = get_polygons(82, 0)
+ count = via5.count
+ logger.info("via5 has #{count} polygons")
+ polygons_count += count
- metaltop_drawn = get_polygons(53 , 0 )
- count = metaltop_drawn.count()
- logger.info("metaltop_drawn has %d polygons" % [count])
- polygons_count += count
+ metaltop_drawn = get_polygons(53, 0)
+ count = metaltop_drawn.count
+ logger.info("metaltop_drawn has #{count} polygons")
+ polygons_count += count
- metaltop_dummy = get_polygons(53 , 4 )
- count = metaltop_dummy.count()
- logger.info("metaltop_dummy has %d polygons" % [count])
- polygons_count += count
+ metaltop_dummy = get_polygons(53, 4)
+ count = metaltop_dummy.count
+ logger.info("metaltop_dummy has #{count} polygons")
+ polygons_count += count
metaltop = metaltop_drawn + metaltop_dummy
- metaltop_label = get_polygons(53 , 10)
- count = metaltop_label.count()
- logger.info("metaltop_label has %d polygons" % [count])
- polygons_count += count
+ metaltop_label = get_polygons(53, 10)
+ count = metaltop_label.count
+ logger.info("metaltop_label has #{count} polygons")
+ polygons_count += count
- metaltop_slot = get_polygons(53 , 3 )
- count = metaltop_slot.count()
- logger.info("metaltop_slot has %d polygons" % [count])
- polygons_count += count
+ metaltop_slot = get_polygons(53, 3)
+ count = metaltop_slot.count
+ logger.info("metaltop_slot has #{count} polygons")
+ polygons_count += count
- metalt_blk = get_polygons(53 , 5 )
- count = metalt_blk.count()
- logger.info("metalt_blk has %d polygons" % [count])
- polygons_count += count
+ metalt_blk = get_polygons(53, 5)
+ count = metalt_blk.count
+ logger.info("metalt_blk has #{count} polygons")
+ polygons_count += count
top_via = via5
topmin1_via = via4
top_metal = metaltop
topmin1_metal = metal5
else
- logger.error("Unknown metal stack %s" % [METAL_LEVEL])
+ logger.error("Unknown metal stack #{METAL_LEVEL}")
raise
end
end
end
end
-pad = get_polygons(37 , 0 )
-count = pad.count()
-logger.info("pad has %d polygons" % [count])
-polygons_count += count
-
-ubmpperi = get_polygons(183, 0 )
-count = ubmpperi.count()
-logger.info("ubmpperi has %d polygons" % [count])
-polygons_count += count
-
-ubmparray = get_polygons(184, 0 )
-count = ubmparray.count()
-logger.info("ubmparray has %d polygons" % [count])
-polygons_count += count
-
-ubmeplate = get_polygons(185, 0 )
-count = ubmeplate.count()
-logger.info("ubmeplate has %d polygons" % [count])
-polygons_count += count
-
-metal1_res = get_polygons(110, 11)
-count = metal1_res.count()
-logger.info("metal1_res has %d polygons" % [count])
-polygons_count += count
-
-metal2_res = get_polygons(110, 12)
-count = metal2_res.count()
-logger.info("metal2_res has %d polygons" % [count])
-polygons_count += count
-
-metal3_res = get_polygons(110, 13)
-count = metal3_res.count()
-logger.info("metal3_res has %d polygons" % [count])
-polygons_count += count
-
-metal4_res = get_polygons(110, 14)
-count = metal4_res.count()
-logger.info("metal4_res has %d polygons" % [count])
-polygons_count += count
-
-metal5_res = get_polygons(110, 15)
-count = metal5_res.count()
-logger.info("metal5_res has %d polygons" % [count])
-polygons_count += count
-
-metal6_res = get_polygons(110, 16)
-count = metal6_res.count()
-logger.info("metal6_res has %d polygons" % [count])
-polygons_count += count
-
-pr_bndry = get_polygons(0 , 0 )
-count = pr_bndry.count()
-logger.info("pr_bndry has %d polygons" % [count])
-polygons_count += count
-
-border = get_polygons(63 , 0 )
-count = border.count()
-logger.info("border has %d polygons" % [count])
-polygons_count += count
+pad = get_polygons(37, 0)
+count = pad.count
+logger.info("pad has #{count} polygons")
+polygons_count += count
+
+ubmpperi = get_polygons(183, 0)
+count = ubmpperi.count
+logger.info("ubmpperi has #{count} polygons")
+polygons_count += count
+
+ubmparray = get_polygons(184, 0)
+count = ubmparray.count
+logger.info("ubmparray has #{count} polygons")
+polygons_count += count
+
+ubmeplate = get_polygons(185, 0)
+count = ubmeplate.count
+logger.info("ubmeplate has #{count} polygons")
+polygons_count += count
+
+metal1_res = get_polygons(110, 11)
+count = metal1_res.count
+logger.info("metal1_res has #{count} polygons")
+polygons_count += count
+
+metal2_res = get_polygons(110, 12)
+count = metal2_res.count
+logger.info("metal2_res has #{count} polygons")
+polygons_count += count
+
+metal3_res = get_polygons(110, 13)
+count = metal3_res.count
+logger.info("metal3_res has #{count} polygons")
+polygons_count += count
+
+metal4_res = get_polygons(110, 14)
+count = metal4_res.count
+logger.info("metal4_res has #{count} polygons")
+polygons_count += count
+
+metal5_res = get_polygons(110, 15)
+count = metal5_res.count
+logger.info("metal5_res has #{count} polygons")
+polygons_count += count
+
+metal6_res = get_polygons(110, 16)
+count = metal6_res.count
+logger.info("metal6_res has #{count} polygons")
+polygons_count += count
+
+pr_bndry = get_polygons(0, 0)
+count = pr_bndry.count
+logger.info("pr_bndry has #{count} polygons")
+polygons_count += count
+
+border = get_polygons(63, 0)
+count = border.count
+logger.info("border has #{count} polygons")
+polygons_count += count
logger.info("Total no. of polygons in the design is #{polygons_count}")
-logger.info("Starting deriving base layers.")
+logger.info('Starting deriving base layers.')
#=====================================================
#------------- BASE LAYERS DERIVATIONS ---------------
@@ -957,35 +865,35 @@ nwell_n_dn = nwell.not_interacting(dnwell)
if CONNECTIVITY_RULES
- logger.info("Construct connectivity for the design.")
+ logger.info('Construct connectivity for the design.')
- connect(dnwell, ncomp)
+ connect(dnwell, ncomp)
connect(ncomp, contact)
connect(pcomp, contact)
- connect(lvpwell_out, pcomp)
+ connect(lvpwell_out, pcomp)
connect(lvpwell_dn, pcomp)
- connect(nwell, ncomp)
- connect(natcomp, contact)
- connect(mvsd, ncomp)
- connect(mvpsd, pcomp)
- connect(contact, metal1)
+ connect(nwell, ncomp)
+ connect(natcomp, contact)
+ connect(mvsd, ncomp)
+ connect(mvpsd, pcomp)
+ connect(contact, metal1)
connect(metal1, via1)
connect(via1, metal2)
- if METAL_LEVEL != "2LM"
+ if METAL_LEVEL != '2LM'
connect(metal2, via2)
connect(via2, metal3)
- if METAL_LEVEL != "3LM"
+ if METAL_LEVEL != '3LM'
connect(metal3, via3)
connect(via3, metal4)
-
- if METAL_LEVEL != "4LM"
+
+ if METAL_LEVEL != '4LM'
connect(metal4, via4)
connect(via4, metal5)
- if METAL_LEVEL != "5LM"
+ if METAL_LEVEL != '5LM'
connect(metal5, via5)
connect(via5, metaltop)
end
@@ -993,21 +901,20 @@ if CONNECTIVITY_RULES
end
end
-end #CONNECTIVITY_RULES
+end
#================================================
#------------ PRE-DEFINED FUNCTIONS -------------
#================================================
-def conn_space(layer,conn_val,not_conn_val, mode)
- if conn_val > not_conn_val
- raise "ERROR : Wrong connectivity implementation"
- end
+def conn_space(layer, conn_val, not_conn_val, mode)
+ raise 'ERROR : Wrong connectivity implementation' if conn_val > not_conn_val
+
connected_output = layer.space(conn_val.um, mode).polygons(0.001)
unconnected_errors_unfiltered = layer.space(not_conn_val.um, mode)
singularity_errors = layer.space(0.001.um)
# Filter out the errors arising from the same net
- unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors = DRC::DRCLayer.new(self, RBA::EdgePairs.new)
unconnected_errors_unfiltered.data.each do |ep|
net1 = l2n_data.probe_net(layer.data, ep.first.p1)
net2 = l2n_data.probe_net(layer.data, ep.second.p1)
@@ -1019,17 +926,16 @@ def conn_space(layer,conn_val,not_conn_val, mode)
end
end
unconnected_output = unconnected_errors.polygons.or(singularity_errors.polygons(0.001))
- return connected_output, unconnected_output
+ [connected_output, unconnected_output]
end
-def conn_separation(layer1, layer2, conn_val,not_conn_val, mode)
- if conn_val > not_conn_val
- raise "ERROR : Wrong connectivity implementation"
- end
+def conn_separation(layer1, layer2, conn_val, not_conn_val, mode)
+ raise 'ERROR : Wrong connectivity implementation' if conn_val > not_conn_val
+
connected_output = layer1.separation(layer2, conn_val.um, mode).polygons(0.001)
unconnected_errors_unfiltered = layer1.separation(layer2, not_conn_val.um, mode)
# Filter out the errors arising from the same net
- unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors = DRC::DRCLayer.new(self, RBA::EdgePairs.new)
unconnected_errors_unfiltered.data.each do |ep|
net1 = l2n_data.probe_net(layer1.data, ep.first.p1)
net2 = l2n_data.probe_net(layer2.data, ep.second.p1)
@@ -1041,30 +947,24 @@ def conn_separation(layer1, layer2, conn_val,not_conn_val, mode)
end
end
unconnected_output = unconnected_errors.polygons(0.001)
- return connected_output, unconnected_output
+ [connected_output, unconnected_output]
end
# === IMPLICIT EXTRACTION ===
if CONNECTIVITY_RULES
- logger.info("Connectivity rules enabled, Netlist object will be generated.")
+ logger.info('Connectivity rules enabled, Netlist object will be generated.')
netlist
-end #CONNECTIVITY_RULES
+end
# === LAYOUT EXTENT ===
CHIP = extent.sized(0.0)
-logger.info("Total area of the design is #{CHIP.area()} um^2.")
+logger.info("Total area of the design is #{CHIP.area} um^2.")
#================================================
#----------------- MAIN RUNSET ------------------
#================================================
-logger.info("Starting GF180MCU DRC rules.")
-if FEOL
- logger.info("Running all FEOL rules")
-end #FEOL
-
-if BEOL
- logger.info("Running all BEOL rules")
-end #BEOL
-
+logger.info('Starting GF180MCU DRC rules.')
+logger.info('Running all FEOL rules') if FEOL
+logger.info('Running all BEOL rules') if BEOL
diff --git a/klayout/drc/rule_decks/mcell.drc b/klayout/drc/rule_decks/mcell.drc
deleted file mode 100644
index 70a0c585..00000000
--- a/klayout/drc/rule_decks/mcell.drc
+++ /dev/null
@@ -1,47 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-if FEOL
- #================================================
- #---------------------MCELL----------------------
- #================================================
-
- # Rule MC.1: min. mcell width is 0.4µm
- logger.info("Executing rule MC.1")
- mc1_l1 = mcell_feol_mk.width(0.4.um, euclidian).polygons(0.001)
- mc1_l1.output("MC.1", "MC.1 : min. mcell width : 0.4µm")
- mc1_l1.forget
-
- # Rule MC.2: min. mcell spacing is 0.4µm
- logger.info("Executing rule MC.2")
- mc2_l1 = mcell_feol_mk.space(0.4.um, euclidian).polygons(0.001)
- mc2_l1.output("MC.2", "MC.2 : min. mcell spacing : 0.4µm")
- mc2_l1.forget
-
- # Rule MC.3: Minimum Mcell area is 0.35µm²
- logger.info("Executing rule MC.3")
- mc3_l1 = mcell_feol_mk.with_area(nil, 0.35.um)
- mc3_l1.output("MC.3", "MC.3 : Minimum Mcell area : 0.35µm²")
- mc3_l1.forget
-
- # Rule MC.4: Minimum area enclosed by Mcell is 0.35µm²
- logger.info("Executing rule MC.4")
- mc4_l1 = mcell_feol_mk.holes.with_area(nil, 0.35.um)
- mc4_l1.output("MC.4", "MC.4 : Minimum area enclosed by Mcell : 0.35µm²")
- mc4_l1.forget
-end #FEOL
-
-
diff --git a/klayout/drc/rule_decks/otp_mk.drc b/klayout/drc/rule_decks/otp_mk.drc
deleted file mode 100644
index ce62944c..00000000
--- a/klayout/drc/rule_decks/otp_mk.drc
+++ /dev/null
@@ -1,143 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-if FEOL
- #================================================
- #---------------------OTP_MK---------------------
- #================================================
-
- logger.info("Starting OTP_MK derivations")
-
- comp_otp = comp.and(otp_mk)
- poly_otp = poly2.and(otp_mk)
- sab_otp = sab.and(otp_mk)
-
- # Rule O.DF.3a: Min. COMP Space. P-substrate tap (PCOMP outside NWELL) can be butted for different voltage devices as the potential is same. is 0.24µm
- logger.info("Executing rule O.DF.3a")
- odf3a_l1 = comp_otp.space(0.24.um, euclidian)
- odf3a_l1.output("O.DF.3a", "O.DF.3a : Min. COMP Space. P-substrate tap (PCOMP outside NWELL) can be butted for different voltage devices as the potential is same. : 0.24µm")
- odf3a_l1.forget
-
- # Rule O.DF.6: Min. COMP extend beyond poly2 (it also means source/drain overhang). is 0.22µm
- logger.info("Executing rule O.DF.6")
- odf6_l1 = poly2.and(otp_mk).enclosed(comp_otp, 0.22.um, projection)
- odf6_l1.output("O.DF.6", "O.DF.6 : Min. COMP extend beyond poly2 (it also means source/drain overhang). : 0.22µm")
- odf6_l1.forget
-
- # Rule O.DF.9: Min. COMP area (um2). is 0.1444µm²
- logger.info("Executing rule O.DF.9")
- odf9_l1 = comp_otp.with_area(nil, 0.1444.um)
- odf9_l1.output("O.DF.9", "O.DF.9 : Min. COMP area (um2). : 0.1444µm²")
- odf9_l1.forget
-
- # Rule O.PL.2: Min. poly2 width. is 0.22µm
- logger.info("Executing rule O.PL.2")
- opl2_l1 = poly_otp.edges.and(tgate.edges).width(0.22.um, euclidian)
- opl2_l1.output("O.PL.2", "O.PL.2 : Min. poly2 width. : 0.22µm")
- opl2_l1.forget
-
- # Rule O.PL.3a: Min. poly2 Space on COMP. is 0.18µm
- logger.info("Executing rule O.PL.3a")
- opl3a_l1 = (tgate).or(poly2.not(comp)).and(otp_mk).space(0.18.um, euclidian)
- opl3a_l1.output("O.PL.3a", "O.PL.3a : Min. poly2 Space on COMP. : 0.18µm")
- opl3a_l1.forget
-
- # Rule O.PL.4: Min. extension beyond COMP to form Poly2 end cap. is 0.14µm
- logger.info("Executing rule O.PL.4")
- opl4_l1 = comp_otp.enclosed(poly_otp, 0.14.um, euclidian)
- opl4_l1.output("O.PL.4", "O.PL.4 : Min. extension beyond COMP to form Poly2 end cap. : 0.14µm")
- opl4_l1.forget
-
- # rule O.PL.5a is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_10.html
-
- # rule O.PL.5b is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_10.html
-
- # Rule O.SB.2: Min. salicide Block Space. is 0.28µm
- logger.info("Executing rule O.SB.2")
- osb2_l1 = sab_otp.space(0.28.um, euclidian)
- osb2_l1.output("O.SB.2", "O.SB.2 : Min. salicide Block Space. : 0.28µm")
- osb2_l1.forget
-
- # Rule O.SB.3: Min. space from salicide block to unrelated COMP. is 0.09µm
- logger.info("Executing rule O.SB.3")
- osb3_l1 = sab.outside(comp).and(otp_mk).separation(comp.outside(sab), 0.09.um, euclidian).polygons(0.001)
- osb3_l1.output("O.SB.3", "O.SB.3 : Min. space from salicide block to unrelated COMP. : 0.09µm")
- osb3_l1.forget
-
- # Rule O.SB.4: Min. space from salicide block to contact.
- logger.info("Executing rule O.SB.4")
- osb4_l1 = sab_otp.separation(contact, 0.03.um, euclidian).polygons(0.001).or(sab_otp.and(contact))
- osb4_l1.output("O.SB.4", "O.SB.4 : Min. space from salicide block to contact.")
- osb4_l1.forget
-
- # rule O.SB.5a is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_10.html
-
- # Rule O.SB.5b_LV: Min. space from salicide block to unrelated Poly2 on COMP. is 0.1µm
- logger.info("Executing rule O.SB.5b_LV")
- osb5b_l1 = sab_otp.outside(tgate).separation(tgate.outside(sab), 0.1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
- osb5b_l1.output("O.SB.5b_LV", "O.SB.5b_LV : Min. space from salicide block to unrelated Poly2 on COMP. : 0.1µm")
- osb5b_l1.forget
-
- # rule O.SB.5b_MV is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_10.html
-
- # Rule O.SB.9: Min. salicide block extension beyond unsalicided Poly2. is 0.1µm
- logger.info("Executing rule O.SB.9")
- osb9_l1 = poly_otp.enclosed(sab_otp, 0.1.um, euclidian)
- osb9_l1.output("O.SB.9", "O.SB.9 : Min. salicide block extension beyond unsalicided Poly2. : 0.1µm")
- osb9_l1.forget
-
- # Rule O.SB.11: Min. salicide block overlap with COMP. is 0.04µm
- logger.info("Executing rule O.SB.11")
- osb11_l1 = sab_otp.overlap(comp, 0.04.um, euclidian)
- osb11_l1.output("O.SB.11", "O.SB.11 : Min. salicide block overlap with COMP. : 0.04µm")
- osb11_l1.forget
-
- # rule O.SB.12 is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_10.html
-
- # Rule O.SB.13_LV: Min. area of silicide block (um2). is 1.488µm²
- logger.info("Executing rule O.SB.13_LV")
- osb13_l1 = sab_otp.with_area(nil, 1.488.um).not_interacting(v5_xtor).not_interacting(dualgate)
- osb13_l1.output("O.SB.13_LV", "O.SB.13_LV : Min. area of silicide block (um2). : 1.488µm²")
- osb13_l1.forget
-
- # Rule O.SB.13_MV: Min. area of silicide block (um2). is 2µm²
- logger.info("Executing rule O.SB.13_MV")
- osb13_l1 = sab_otp.and(v5_xtor).with_area(nil, 2.um)
- osb13_l1.output("O.SB.13_MV", "O.SB.13_MV : Min. area of silicide block (um2). : 2µm²")
- osb13_l1.forget
-
- # rule O.SB.15b is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_10.html
-
- # Rule O.CO.7: Min. space from COMP contact to Poly2 on COMP. is 0.13µm
- logger.info("Executing rule O.CO.7")
- oco7_l1 = contact.not_outside(comp_otp).separation(tgate.and(otp_mk), 0.13.um, euclidian)
- oco7_l1.output("O.CO.7", "O.CO.7 : Min. space from COMP contact to Poly2 on COMP. : 0.13µm")
- oco7_l1.forget
-
- # Rule O.PL.ORT: Orientation-restricted gates must have the gate width aligned along the X-axis (poly line running horizontally) in reference to wafer notch down. is 0µm
- logger.info("Executing rule O.PL.ORT")
- oplort_l1 = comp_otp.not(poly_otp).edges.and(tgate.edges).without_angle(0.um)
- oplort_l1.output("O.PL.ORT", "O.PL.ORT : Orientation-restricted gates must have the gate width aligned along the X-axis (poly line running horizontally) in reference to wafer notch down. : 0µm")
- oplort_l1.forget
-
-end #FEOL
-
diff --git a/klayout/drc/rule_decks/pres.drc b/klayout/drc/rule_decks/pres.drc
deleted file mode 100644
index c6e64c72..00000000
--- a/klayout/drc/rule_decks/pres.drc
+++ /dev/null
@@ -1,99 +0,0 @@
-################################################################################################
-# Copyright 2022 GlobalFoundries PDK Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-################################################################################################
-
-if FEOL
- #================================================
- #----------------P+ POLY RESISTOR----------------
- #================================================
-
- logger.info("Starting PRES derivations")
- pres_poly = poly2.and(pplus).interacting(sab).interacting(res_mk).not_interacting(resistor)
-
- # Rule PRES.1: Minimum width of Poly2 resistor. is 0.8µm
- logger.info("Executing rule PRES.1")
- pres1_l1 = pres_poly.width(0.8.um, euclidian)
- pres1_l1.output("PRES.1", "PRES.1 : Minimum width of Poly2 resistor. : 0.8µm")
- pres1_l1.forget
-
- # Rule PRES.2: Minimum space between Poly2 resistors. is 0.4µm
- logger.info("Executing rule PRES.2")
- pres2_l1 = pres_poly.isolated(0.4.um, euclidian)
- pres2_l1.output("PRES.2", "PRES.2 : Minimum space between Poly2 resistors. : 0.4µm")
- pres2_l1.forget
-
- # Rule PRES.3: Minimum space from Poly2 resistor to COMP.
- logger.info("Executing rule PRES.3")
- pres3_l1 = pres_poly.separation(comp, 0.6.um, euclidian).polygons(0.001)
- pres3_l2 = comp.not_outside(pres_poly)
- pres3_l = pres3_l1.or(pres3_l2)
- pres3_l.output("PRES.3", "PRES.3 : Minimum space from Poly2 resistor to COMP.")
- pres3_l.forget
- pres3_l1.forget
- pres3_l2.forget
-
- # Rule PRES.4: Minimum space from Poly2 resistor to unrelated Poly2. is 0.6µm
- logger.info("Executing rule PRES.4")
- pres4_l1 = pres_poly.separation(poly2.not_interacting(sab), 0.6.um, euclidian)
- pres4_l1.output("PRES.4", "PRES.4 : Minimum space from Poly2 resistor to unrelated Poly2. : 0.6µm")
- pres4_l1.forget
-
- # Rule PRES.5: Minimum Plus implant overlap of Poly2 resistor. is 0.3µm
- logger.info("Executing rule PRES.5")
- pres5_l1 = pres_poly.enclosed(pplus, 0.3.um, euclidian).polygons(0.001)
- pres5_l2 = pres_poly.not_outside(pplus).not(pplus)
- pres5_l = pres5_l1.or(pres5_l2)
- pres5_l.output("PRES.5", "PRES.5 : Minimum Plus implant overlap of Poly2 resistor. : 0.3µm")
- pres5_l1.forget
- pres5_l2.forget
- pres5_l.forget
-
- # Rule PRES.6: Minimum salicide block overlap of Poly2 resistor in width direction. is 0.28µm
- logger.info("Executing rule PRES.6")
- pres6_l1 = pres_poly.enclosed(sab,0.28.um)
- pres6_l1.output("PRES.6", "PRES.6 : Minimum salicide block overlap of Poly2 resistor in width direction. : 0.28µm")
- pres6_l1.forget
-
- # Rule PRES.7: Space from salicide block to contact on Poly2 resistor.
- logger.info("Executing rule PRES.7")
- pres7_l1 = contact.and(pres_poly).separation(sab,0.22.um).polygons(0.001)
- pres7_l2 = contact.and(pres_poly).interacting(sab)
- pres7_l = pres7_l1.or(pres7_l2)
- pres7_l.output("PRES.7", "PRES.7 : Space from salicide block to contact on Poly2 resistor.")
- pres7_l1.forget
- pres7_l2.forget
-
- # Rule PRES.8 is not a DRC check
- ## Please refer to https://gf180mcu-pdk.readthedocs.io/en/latest/physical_verification/design_manual/drm_10_01.html#p-poly-resistor-pres
-
- # Rule PRES.9a: Pplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2.
- logger.info("Executing rule PRES.9a")
- mk_pres9a = res_mk.edges.not(poly2.and(pplus).and(sab).edges).inside_part(poly2)
- pres9a_l1 = res_mk.interacting(pres_poly).interacting(mk_pres9a)
- pres9a_l1.output("PRES.9a", "PRES.9a : Pplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2.")
- pres9a_l1.forget
- mk_pres9a.forget
-
- # Rule PRES.9b: If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um.
- ## then the minimum spacing to adjacent RES_MK layer. is 20µm
- logger.info("Executing rule PRES.9b")
- pres9b = res_mk.with_area(15000.001.um,nil).edges.with_length(80.001.um,nil)
- pres9b_l1 = pres9b.separation(res_mk.edges, 20.um)
- pres9b_l1.output("PRES.9b", "PRES.9b : If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um. then the minimum spacing to adjacent RES_MK layer. : 20µm")
- pres9b_l1.forget
- pres9b.forget
- pres_poly.forget
-
-end #PRES
diff --git a/klayout/drc/rule_decks/tail.drc b/klayout/drc/rule_decks/tail.drc
index b4ef0bca..a9a22a63 100644
--- a/klayout/drc/rule_decks/tail.drc
+++ b/klayout/drc/rule_decks/tail.drc
@@ -14,10 +14,6 @@
# limitations under the License.
################################################################################################
-
-
exec_end_time = Time.now
run_time = exec_end_time - exec_start_time
-logger.info("%s DRC Total Run time %f seconds" % [table_name, run_time])
-
-
+logger.info("#{$table_name} DRC Total Run time #{run_time} seconds")
diff --git a/klayout/drc/testing/run_regression.py b/klayout/drc/testing/run_regression.py
index a3ba601c..f8109fca 100644
--- a/klayout/drc/testing/run_regression.py
+++ b/klayout/drc/testing/run_regression.py
@@ -444,7 +444,7 @@ def parse_existing_rules(rule_deck_path, output_path, target_table=None):
with open(runset, "r") as f:
for line in f:
if ".output" in line:
- line_list = line.split('"')
+ line_list = line.split("'")
rule_info = dict()
rule_info["table_name"] = os.path.basename(runset).replace(
".drc", ""
diff --git a/klayout/drc/testing/testcases/unit/drc_bjt.gds b/klayout/drc/testing/testcases/unit/drc_bjt.gds
deleted file mode 100644
index 2c180521..00000000
Binary files a/klayout/drc/testing/testcases/unit/drc_bjt.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/drc_bjt.svg b/klayout/drc/testing/testcases/unit/drc_bjt.svg
deleted file mode 100644
index e183e172..00000000
--- a/klayout/drc/testing/testcases/unit/drc_bjt.svg
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/dummy_exclude.gds b/klayout/drc/testing/testcases/unit/dummy_exclude.gds
deleted file mode 100644
index 0e3dd515..00000000
Binary files a/klayout/drc/testing/testcases/unit/dummy_exclude.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/dummy_exclude.svg b/klayout/drc/testing/testcases/unit/dummy_exclude.svg
deleted file mode 100644
index dead3fd1..00000000
--- a/klayout/drc/testing/testcases/unit/dummy_exclude.svg
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/efuse.gds b/klayout/drc/testing/testcases/unit/efuse.gds
deleted file mode 100644
index 9af4699e..00000000
Binary files a/klayout/drc/testing/testcases/unit/efuse.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/efuse.svg b/klayout/drc/testing/testcases/unit/efuse.svg
deleted file mode 100644
index d63437b6..00000000
--- a/klayout/drc/testing/testcases/unit/efuse.svg
+++ /dev/null
@@ -1,3817 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/hres.gds b/klayout/drc/testing/testcases/unit/hres.gds
deleted file mode 100644
index 9418f5b5..00000000
Binary files a/klayout/drc/testing/testcases/unit/hres.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/hres.svg b/klayout/drc/testing/testcases/unit/hres.svg
deleted file mode 100644
index 2e0c6c93..00000000
--- a/klayout/drc/testing/testcases/unit/hres.svg
+++ /dev/null
@@ -1,935 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/lres.gds b/klayout/drc/testing/testcases/unit/lres.gds
deleted file mode 100644
index c5885840..00000000
Binary files a/klayout/drc/testing/testcases/unit/lres.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/lres.svg b/klayout/drc/testing/testcases/unit/lres.svg
deleted file mode 100644
index dcc0ddad..00000000
--- a/klayout/drc/testing/testcases/unit/lres.svg
+++ /dev/null
@@ -1,690 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/lvs_bjt.gds b/klayout/drc/testing/testcases/unit/lvs_bjt.gds
deleted file mode 100644
index a817e526..00000000
Binary files a/klayout/drc/testing/testcases/unit/lvs_bjt.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/lvs_bjt.svg b/klayout/drc/testing/testcases/unit/lvs_bjt.svg
deleted file mode 100644
index 4228da71..00000000
--- a/klayout/drc/testing/testcases/unit/lvs_bjt.svg
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/mcell.gds b/klayout/drc/testing/testcases/unit/mcell.gds
deleted file mode 100644
index c317b75e..00000000
Binary files a/klayout/drc/testing/testcases/unit/mcell.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/mcell.svg b/klayout/drc/testing/testcases/unit/mcell.svg
deleted file mode 100644
index 01d5eb71..00000000
--- a/klayout/drc/testing/testcases/unit/mcell.svg
+++ /dev/null
@@ -1,184 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/otp_mk.gds b/klayout/drc/testing/testcases/unit/otp_mk.gds
deleted file mode 100644
index caae0a4f..00000000
Binary files a/klayout/drc/testing/testcases/unit/otp_mk.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/otp_mk.svg b/klayout/drc/testing/testcases/unit/otp_mk.svg
deleted file mode 100644
index ce7b40fe..00000000
--- a/klayout/drc/testing/testcases/unit/otp_mk.svg
+++ /dev/null
@@ -1,1540 +0,0 @@
-
-
\ No newline at end of file
diff --git a/klayout/drc/testing/testcases/unit/pres.gds b/klayout/drc/testing/testcases/unit/pres.gds
deleted file mode 100644
index 6a3a5289..00000000
Binary files a/klayout/drc/testing/testcases/unit/pres.gds and /dev/null differ
diff --git a/klayout/drc/testing/testcases/unit/pres.svg b/klayout/drc/testing/testcases/unit/pres.svg
deleted file mode 100644
index 703e3877..00000000
--- a/klayout/drc/testing/testcases/unit/pres.svg
+++ /dev/null
@@ -1,658 +0,0 @@
-
-
\ No newline at end of file
diff --git a/pdk_regression.yml b/pdk_regression.yml
index 7e29276a..d5216abb 100644
--- a/pdk_regression.yml
+++ b/pdk_regression.yml
@@ -5,6 +5,7 @@ channels:
dependencies:
- klayout
- python
+ - ruby
- pip
- pip:
- -r requirements.txt