| # 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. |
| |
| #============================================================================================================================================================= |
| #--------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (10V LDPMOS) ---------------------------------------------------------- |
| #============================================================================================================================================================= |
| |
| require 'time' |
| require "logger" |
| |
| exec_start_time = Time.now |
| |
| logger = Logger.new(STDOUT) |
| |
| logger.formatter = proc do |severity, datetime, progname, msg| |
| "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg} |
| " |
| end |
| |
| #================================================ |
| #----------------- FILE SETUP ------------------- |
| #================================================ |
| |
| # optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb |
| |
| logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input]) |
| |
| if $input |
| if $topcell |
| source($input, $topcell) |
| else |
| source($input) |
| end |
| end |
| |
| 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) |
| else |
| logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path]) |
| report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb")) |
| end |
| |
| if $thr |
| logger.info("Number of threads to use %s" % [$thr]) |
| threads($thr) |
| else |
| logger.info("Number of threads to use 16") |
| threads(16) |
| end |
| |
| # === TILING MODE === |
| if $run_mode == "tiling" |
| # use a tile size of 1mm - not used in deep mode- |
| # tiles(500.um) |
| # use a tile border of 10 micron: |
| # tile_borders(10.um) |
| tiles(1000) |
| logger.info("Tiling mode is enabled.") |
| |
| elsif $run_mode == "deep" |
| #=== HIER MODE === |
| deep |
| logger.info("deep mode is enabled.") |
| |
| elsif $run_mode == "flat" |
| #=== FLAT MODE === |
| flat |
| logger.info("flat mode is enabled.") |
| |
| else |
| #=== FLAT MODE === |
| flat |
| logger.info("flat mode is enabled.") |
| |
| end # run_mode |
| |
| #================================================ |
| #------------- LAYERS DEFINITIONS --------------- |
| #================================================ |
| |
| v5_xtor = polygons(112, 1 ) |
| dualgate = polygons(55 , 0 ) |
| poly2 = polygons(30 , 0 ) |
| nplus = polygons(32 , 0 ) |
| pplus = polygons(31 , 0 ) |
| comp = polygons(22 , 0 ) |
| dnwell = polygons(12 , 0 ) |
| nwell = polygons(21 , 0 ) |
| sab = polygons(49 , 0 ) |
| contact = polygons(33 , 0 ) |
| metal1 = polygons(34 , 0 ) |
| mvpsd = polygons(11 , 39) |
| ldmos_xtor = polygons(226, 0 ) |
| |
| logger.info("Starting deriving base layers.") |
| #================================================ |
| #------------- LAYERS DERIVATIONS --------------- |
| #================================================ |
| |
| ncomp = comp & nplus |
| pcomp = comp & pplus |
| tgate = poly2 & comp |
| ngate = nplus & tgate |
| pgate = pplus & tgate |
| |
| #================================================ |
| #------------------ SWITCHES -------------------- |
| #================================================ |
| logger.info("Evaluate switches.") |
| |
| # FEOL |
| if $feol == "false" |
| FEOL = $feol |
| logger.info("FEOL is disabled.") |
| else |
| FEOL = "true" |
| logger.info("FEOL is enabled.") |
| end # FEOL |
| |
| # BEOL |
| if $beol == "false" |
| BEOL = $beol |
| logger.info("BEOL is disabled.") |
| else |
| BEOL = "true" |
| logger.info("BEOL is enabled.") |
| end # BEOL |
| |
| # connectivity rules |
| if $conn_drc == "true" |
| CONNECTIVITY_RULES = $conn_drc |
| logger.info("connectivity rules are enabled.") |
| else |
| CONNECTIVITY_RULES = false |
| logger.info("connectivity rules are disabled.") |
| end # connectivity rules |
| |
| # METAL_TOP |
| if $metal_top |
| METAL_TOP = $metal_top |
| else |
| METAL_TOP = "9K" |
| end # METAL_TOP |
| |
| logger.info("METAL_TOP Selected is %s" % [METAL_TOP]) |
| |
| # METAL_LEVEL |
| if $metal_level |
| METAL_LEVEL = $metal_level |
| else |
| METAL_LEVEL = "6LM" |
| end # METAL_LEVEL |
| |
| logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL]) |
| |
| # WEDGE |
| if $wedge == "false" |
| WEDGE = $wedge |
| else |
| WEDGE = "true" |
| end # WEDGE |
| |
| logger.info("Wedge enabled %s" % [WEDGE]) |
| |
| # BALL |
| if $ball == "false" |
| BALL = $ball |
| else |
| BALL = "true" |
| end # BALL |
| |
| logger.info("Ball enabled %s" % [BALL]) |
| |
| # GOLD |
| if $gold == "false" |
| GOLD = $gold |
| else |
| GOLD = "true" |
| end # GOLD |
| |
| logger.info("Gold enabled %s" % [GOLD]) |
| |
| if $mim_option |
| MIM_OPTION = $mim_option |
| else |
| MIM_OPTION = "Nan" |
| end |
| |
| logger.info("MIM Option selected %s" % [MIM_OPTION]) |
| |
| # OFFGRID |
| if $offgrid == "false" |
| OFFGRID = false |
| else |
| OFFGRID = true |
| end # OFFGRID |
| |
| logger.info("Offgrid enabled %s" % [OFFGRID]) |
| #================================================ |
| #------------- LAYERS CONNECTIONS --------------- |
| #================================================ |
| |
| if CONNECTIVITY_RULES |
| |
| logger.info("Construct connectivity for the design.") |
| |
| connect(dnwell, ncomp) |
| connect(ncomp, contact) |
| connect(pcomp, contact) |
| connect(lvpwell, ncomp) |
| connect(nwell, ncomp) |
| connect(natcompsd, contact) |
| connect(mvsd, ncomp) |
| connect(mvpsd, pcomp) |
| connect(contact, metal1) |
| connect(metal1, via1) |
| connect(via1, metal2) |
| connect(metal2, via2) |
| connect(via2, metal3) |
| connect(metal3, via3) |
| connect(via3, metal4) |
| connect(metal4, via4) |
| connect(via4, metal5) |
| connect(metal5, via5) |
| connect(via5, metaltop) |
| |
| end #CONNECTIVITY_RULES |
| |
| #================================================ |
| #------------ 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 |
| 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_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) |
| if !net1 || !net2 |
| puts "Should not happen ..." |
| elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id |
| # unconnected |
| unconnected_errors.data.insert(ep) |
| end |
| end |
| unconnected_output = unconnected_errors.polygons.or(singularity_errors.polygons(0.001)) |
| return 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 |
| 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_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) |
| if !net1 || !net2 |
| puts "Should not happen ..." |
| elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id |
| # unconnected |
| unconnected_errors.data.insert(ep) |
| end |
| end |
| unconnected_output = unconnected_errors.polygons(0.001) |
| return connected_output, unconnected_output |
| end |
| |
| # === IMPLICIT EXTRACTION === |
| if CONNECTIVITY_RULES |
| logger.info("Connectivity rules enabled, Netlist object will be generated.") |
| netlist |
| end #CONNECTIVITY_RULES |
| |
| # === LAYOUT EXTENT === |
| CHIP = extent.sized(0.0) |
| |
| logger.info("Total area of the design is #{CHIP.area()} um^2.") |
| |
| |
| |
| #================================================ |
| #-------------------10V LDPMOS------------------- |
| #================================================ |
| |
| mdp_source = (pcomp).interacting(poly2.and(dualgate).and(ldmos_xtor).and(mvpsd)).not(poly2) |
| ldpmos = poly2.and(pcomp).and(dualgate).not(mvpsd).inside(ldmos_xtor) |
| # Rule MDP.1: Minimum transistor channel length. is 0.6µm |
| logger.info("Executing rule MDP.1") |
| mdp1_l1 = poly2.and(comp).inside(ldmos_xtor).inside(dualgate).enclosing(mvpsd, 0.6.um, euclidian).polygons(0.001) |
| mdp1_l1.output("MDP.1", "MDP.1 : Minimum transistor channel length. : 0.6µm") |
| mdp1_l1.forget |
| |
| mvpsd_mdp = mvpsd.edges.and(pcomp).and(poly2) |
| # Rule MDP.1a: Max transistor channel length. |
| logger.info("Executing rule MDP.1a") |
| mdp1a_l1 = poly2.edges.and(pcomp).or(mvpsd_mdp).and(ldmos_xtor).and(dualgate).not(pgate.not(mvpsd).edges.interacting(poly2.edges.and(pcomp).or(mvpsd_mdp)).width(20.001.um).edges) |
| mdp1a_l1.output("MDP.1a", "MDP.1a : Max transistor channel length.") |
| mdp1a_l1.forget |
| |
| mvpsd_mdp.forget |
| # Rule MDP.2: Minimum transistor channel width. is 4µm |
| logger.info("Executing rule MDP.2") |
| mdp2_l1 = poly2.and(comp).inside(ldmos_xtor).inside(dualgate).edges.not(mvpsd).interacting(mvpsd).width(4.um, euclidian).polygons(0.001) |
| mdp2_l1.output("MDP.2", "MDP.2 : Minimum transistor channel width. : 4µm") |
| mdp2_l1.forget |
| |
| mdp3_1 = ldpmos.or(mvpsd).or(mdp_source).not_interacting(ncomp.holes).inside(dualgate).inside(ldmos_xtor) |
| mdp3_2 = ncomp.holes.not_interacting(ncomp.interacting(mdp_source)).not_interacting(mvpsd,1,1).inside(dualgate).inside(ldmos_xtor) |
| # Rule MDP.3: Each LDPMOS shall be surrounded by non-broken Nplus guard ring inside DNWELL |
| logger.info("Executing rule MDP.3") |
| mdp3_l1 = mdp3_1.or(mdp3_2) |
| mdp3_l1.output("MDP.3", "MDP.3 : Each LDPMOS shall be surrounded by non-broken Nplus guard ring inside DNWELL") |
| mdp3_l1.forget |
| |
| ncomp_mdp3ai = ncomp.not_interacting(pcomp).inside(ldmos_xtor).inside(dualgate) |
| # Rule MDP.3ai: Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap non-butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed. |
| logger.info("Executing rule MDP.3ai") |
| mdp3ai_l1 = ncomp_mdp3ai.separation(mvpsd, 1.um, euclidian).polygons(0.001).or(mvpsd.interacting(ncomp_mdp3ai)) |
| mdp3ai_l1.output("MDP.3ai", "MDP.3ai : Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap non-butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed.") |
| mdp3ai_l1.forget |
| |
| ncomp_mdp3ai.forget |
| ncomp_mdp3aii = ncomp.interacting(pcomp).inside(ldmos_xtor).inside(dualgate) |
| # Rule MDP.3aii: Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed. |
| logger.info("Executing rule MDP.3aii") |
| mdp3aii_l1 = ncomp_mdp3aii.separation(mvpsd, 0.92.um, euclidian).polygons(0.001).or(mvpsd.interacting(ncomp_mdp3aii)) |
| mdp3aii_l1.output("MDP.3aii", "MDP.3aii : Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed.") |
| mdp3aii_l1.forget |
| |
| ncomp_mdp3aii.forget |
| ncomp_mdp3b = ncomp.inside(ldmos_xtor).inside(dualgate) |
| pcomp_mdp3b = pcomp.inside(dnwell).inside(ldmos_xtor).inside(dualgate) |
| # Rule MDP.3b: Min NCOMP (Nplus AND COMP) space to PCOMP in DNWELL (Pplus AND COMP AND DNWELL). Use butted source and DNWELL contacts otherwise and that is best for Latch-up immunity as well. is 0.4µm |
| logger.info("Executing rule MDP.3b") |
| mdp3b_l1 = ncomp_mdp3b.not(poly2).not(mvpsd).separation(pcomp_mdp3b.not(poly2).not(mvpsd), 0.4.um, euclidian).polygons(0.001) |
| mdp3b_l1.output("MDP.3b", "MDP.3b : Min NCOMP (Nplus AND COMP) space to PCOMP in DNWELL (Pplus AND COMP AND DNWELL). Use butted source and DNWELL contacts otherwise and that is best for Latch-up immunity as well. : 0.4µm") |
| mdp3b_l1.forget |
| |
| ncomp_mdp3b.forget |
| pcomp_mdp3b.forget |
| # Rule MDP.3c: Maximum distance of the nearest edge of the DNWELL tab (NCOMP inside DNWELL) from PCOMP edge (PCOMP inside DNWELL). is 15µm |
| logger.info("Executing rule MDP.3c") |
| mdp3c_l1 = ncomp.inside(dnwell).inside(ldmos_xtor).inside(dualgate).not_interacting(ncomp.inside(dnwell).drc(separation(pcomp.inside(dnwell)) <= 15.um).first_edges,4) |
| mdp3c_l1.output("MDP.3c", "MDP.3c : Maximum distance of the nearest edge of the DNWELL tab (NCOMP inside DNWELL) from PCOMP edge (PCOMP inside DNWELL). : 15µm") |
| mdp3c_l1.forget |
| |
| # Rule MDP.3d: The metal connection for the Nplus guard ring recommended to be continuous. The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability. is 10µm |
| logger.info("Executing rule MDP.3d") |
| mdp3d_l1 = ncomp.interacting(ldmos_xtor.interacting(mvpsd)).interacting(dualgate).not(metal1).edges.not(metal1).with_length(10.001.um, nil) |
| mdp3d_l1.output("MDP.3d", "MDP.3d : The metal connection for the Nplus guard ring recommended to be continuous. The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability. : 10µm") |
| mdp3d_l1.forget |
| |
| mdp4_metal = pcomp.not_interacting(mvpsd).interacting(ldmos_xtor.interacting(mvpsd)).interacting(dualgate).not(metal1).edges.not(metal1).with_length(10.001.um, nil) |
| # Rule MDP.4: DNWELL covering LDPMOS shall be surrounded by non broken Pplus guard. The metal connection for the Pplus guard ring recommended to be continuous, The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability. |
| logger.info("Executing rule MDP.4") |
| mdp4_l1 = pcomp.interacting(metal1).not_interacting(pcomp.holes).edges.and(ldmos_xtor).and(dualgate).or(mdp4_metal) |
| mdp4_l1.output("MDP.4", "MDP.4 : DNWELL covering LDPMOS shall be surrounded by non broken Pplus guard. The metal connection for the Pplus guard ring recommended to be continuous, The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability.") |
| mdp4_l1.forget |
| |
| mdp4_metal.forget |
| # Rule MDP.4a: Min PCOMP (Pplus AND COMP) space to DNWELL. is 2.5µm |
| logger.info("Executing rule MDP.4a") |
| mdp4a_l1 = pcomp.inside(ldmos_xtor).inside(dualgate).separation(dnwell.inside(ldmos_xtor).inside(dualgate), 2.5.um, euclidian).polygons(0.001) |
| mdp4a_l1.output("MDP.4a", "MDP.4a : Min PCOMP (Pplus AND COMP) space to DNWELL. : 2.5µm") |
| mdp4a_l1.forget |
| |
| mdp4b_dnwell_edges = dnwell.inside(ldmos_xtor).inside(dualgate).edges.centers(0, 0.99) |
| mdp4b_not_error = dnwell.drc(separation(pcomp.inside(ldmos_xtor.interacting(mvpsd)).inside(dualgate).not_interacting(mvpsd), euclidian) <= 15.um).polygons(0.001) |
| # Rule MDP.4b: Maximum distance of the nearest edge of the DNWELL from the PCOMP Guard ring outside DNWELL. is 15µm |
| logger.info("Executing rule MDP.4b") |
| mdp4b_l1 = mdp4b_dnwell_edges.not_interacting(mdp4b_not_error).and(pcomp.holes).extended(0, 0, 0.001, 0.001) |
| mdp4b_l1.output("MDP.4b", "MDP.4b : Maximum distance of the nearest edge of the DNWELL from the PCOMP Guard ring outside DNWELL. : 15µm") |
| mdp4b_l1.forget |
| |
| mdp4b_dnwell_edges.forget |
| mdp4b_not_error.forget |
| # Rule MDP.5: Each LDPMOS shall be covered by Dualgate layer. |
| logger.info("Executing rule MDP.5") |
| mdp5_l1 = pcomp.not(poly2).not(mvpsd).or(pgate.not(mvpsd)).or(pcomp.and(mvpsd)).inside(ldmos_xtor).not_inside(dualgate) |
| mdp5_l1.output("MDP.5", "MDP.5 : Each LDPMOS shall be covered by Dualgate layer.") |
| mdp5_l1.forget |
| |
| # Rule MDP.5a: Minimum Dualgate enclose Plus guarding ring PCOMP (Pplus AND COMP). is 0.5µm |
| logger.info("Executing rule MDP.5a") |
| mdp5a_l1 = dualgate.interacting(ldmos_xtor).enclosing(pcomp.inside(ldmos_xtor), 0.5.um, euclidian).polygons(0.001) |
| mdp5a_l2 = pcomp.inside(ldmos_xtor).not_outside(dualgate.interacting(ldmos_xtor)).not(dualgate.interacting(ldmos_xtor)) |
| mdp5a_l = mdp5a_l1.or(mdp5a_l2) |
| mdp5a_l.output("MDP.5a", "MDP.5a : Minimum Dualgate enclose Plus guarding ring PCOMP (Pplus AND COMP). : 0.5µm") |
| mdp5a_l1.forget |
| mdp5a_l2.forget |
| mdp5a_l.forget |
| |
| # Rule MDP.6: Each LDPMOS shall be covered by LDMOS_XTOR (GDS#226) layer. |
| logger.info("Executing rule MDP.6") |
| mdp6_l1 = mvpsd.not_inside(ldmos_xtor) |
| mdp6_l1.output("MDP.6", "MDP.6 : Each LDPMOS shall be covered by LDMOS_XTOR (GDS#226) layer.") |
| mdp6_l1.forget |
| |
| # Rule MDP.6a: Minimum LDMOS_XTOR enclose Dualgate. |
| logger.info("Executing rule MDP.6a") |
| mdp6a_l1 = ldmos_xtor.not_covering(dualgate) |
| mdp6a_l1.output("MDP.6a", "MDP.6a : Minimum LDMOS_XTOR enclose Dualgate.") |
| mdp6a_l1.forget |
| |
| # Rule MDP.7: Minimum LDMOS_XTOR layer space to Nwell outside LDMOS_XTOR. is 2µm |
| logger.info("Executing rule MDP.7") |
| mdp7_l1 = ldmos_xtor.separation(nwell.outside(ldmos_xtor), 2.um, euclidian).polygons(0.001) |
| mdp7_l1.output("MDP.7", "MDP.7 : Minimum LDMOS_XTOR layer space to Nwell outside LDMOS_XTOR. : 2µm") |
| mdp7_l1.forget |
| |
| # Rule MDP.8: Minimum LDMOS_XTOR layer space to NCOMP outside LDMOS_XTOR. is 1.5µm |
| logger.info("Executing rule MDP.8") |
| mdp8_l1 = ldmos_xtor.separation(ncomp.outside(ldmos_xtor), 1.5.um, euclidian).polygons(0.001) |
| mdp8_l1.output("MDP.8", "MDP.8 : Minimum LDMOS_XTOR layer space to NCOMP outside LDMOS_XTOR. : 1.5µm") |
| mdp8_l1.forget |
| |
| # Rule MDP.9a: Min LDPMOS POLY2 width. is 1.2µm |
| logger.info("Executing rule MDP.9a") |
| mdp9a_l1 = poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).width(1.2.um, euclidian).polygons(0.001) |
| mdp9a_l1.output("MDP.9a", "MDP.9a : Min LDPMOS POLY2 width. : 1.2µm") |
| mdp9a_l1.forget |
| |
| mdp9b_1 = poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).edges.interacting(mvpsd).not(mvpsd).enclosing(comp.edges,0.4.um).edges |
| mdp9b_2 = poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).edges.interacting(mvpsd).not(mvpsd).interacting(pcomp) |
| # Rule MDP.9b: Min POLY2 extension beyond COMP in the width direction of the transistor (other than the LDMOS drain direction). is 0.4µm |
| logger.info("Executing rule MDP.9b") |
| mdp9b_l1 = mdp9b_1.or(mdp9b_2).extended(0,0,0.001,0.001) |
| mdp9b_l1.output("MDP.9b", "MDP.9b : Min POLY2 extension beyond COMP in the width direction of the transistor (other than the LDMOS drain direction). : 0.4µm") |
| mdp9b_l1.forget |
| |
| mdp9b_1.forget |
| mdp9b_2.forget |
| # Rule MDP.9c: Min/Max POLY2 extension beyond COMP on the field towards LDPMOS drain (MVPSD AND COMP AND Pplus NOT POLY2) direction. |
| logger.info("Executing rule MDP.9c") |
| mdp9c_l1 = poly2.edges.in(poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).edges.inside_part(mvpsd)).not_interacting(poly2.drc(enclosing(comp,projection) == 0.2.um)) |
| mdp9c_l1.output("MDP.9c", "MDP.9c : Min/Max POLY2 extension beyond COMP on the field towards LDPMOS drain (MVPSD AND COMP AND Pplus NOT POLY2) direction.") |
| mdp9c_l1.forget |
| |
| # Rule MDP.9d: Min/Max POLY2 on field to LDPMOS drain COMP (MVPSD AND COMP AND Pplus NOT POLY2) space. |
| logger.info("Executing rule MDP.9d") |
| mdp9d_l1 = poly2.inside(dualgate).inside(ldmos_xtor).overlapping(mvpsd.and(pcomp).not(poly2).sized(0.16.um)).or(poly2.inside(dualgate).inside(ldmos_xtor.interacting(mvpsd)).not_interacting(mvpsd.and(pcomp).not(poly2).sized(0.16.um))) |
| mdp9d_l1.output("MDP.9d", "MDP.9d : Min/Max POLY2 on field to LDPMOS drain COMP (MVPSD AND COMP AND Pplus NOT POLY2) space.") |
| mdp9d_l1.forget |
| |
| ldpmos_poly2_gate = poly2.interacting(pgate.and(dualgate).not(mvpsd)) |
| ncomp_not_butted = ncomp.not(pplus).not_interacting(pcomp.not(nplus)).or(ncomp.not(pplus).overlapping(pcomp.not(nplus))) |
| mdp9ei_1 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).separation(ncomp_not_butted, 0.4.um).polygons(0.001) |
| mdp9ei_2 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).and(ncomp_not_butted) |
| # Rule MDP.9ei: Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap non-butted). |
| logger.info("Executing rule MDP.9ei") |
| mdp9ei_l1 = mdp9ei_1.or(mdp9ei_2) |
| mdp9ei_l1.output("MDP.9ei", "MDP.9ei : Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap non-butted).") |
| mdp9ei_l1.forget |
| |
| ncomp_not_butted.forget |
| mdp9ei_1.forget |
| mdp9ei_2.forget |
| ncomp_butted = ncomp.not(pplus).interacting(pcomp.not(nplus)).not_overlapping(pcomp.not(nplus)) |
| mdp9eii_1 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).separation(ncomp_butted, 0.32.um).polygons(0.001) |
| mdp9eii_2 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).and(ncomp_butted) |
| # Rule MDP.9eii: Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap butted). |
| logger.info("Executing rule MDP.9eii") |
| mdp9eii_l1 = mdp9eii_1.or(mdp9eii_2) |
| mdp9eii_l1.output("MDP.9eii", "MDP.9eii : Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap butted).") |
| mdp9eii_l1.forget |
| |
| ncomp_butted.forget |
| mdp9eii_1.forget |
| mdp9eii_2.forget |
| # Rule MDP.9f: Poly2 interconnect is not allowed in LDPMOS region (LDMOS_XTOR marked region). is -µm |
| logger.info("Executing rule MDP.9f") |
| mdp9f_l1 = poly2.not(pplus).inside(dualgate).inside(ldmos_xtor).interacting(poly2.and(pplus).inside(dualgate).inside(ldmos_xtor),2) |
| mdp9f_l1.output("MDP.9f", "MDP.9f : Poly2 interconnect is not allowed in LDPMOS region (LDMOS_XTOR marked region). : -µm") |
| mdp9f_l1.forget |
| |
| # Rule MDP.10: Min/Max MVPSD overlap onto the channel (LDMOS_XTOR AND COMP AND POLY2 AND Pplus). |
| logger.info("Executing rule MDP.10") |
| mdp10_l1 = mvpsd.inside(dualgate).inside(ldmos_xtor).not_interacting(mvpsd.drc(overlap(ldmos_xtor.and(comp).and(poly2).and(pplus),projection) == 0.4)) |
| mdp10_l1.output("MDP.10", "MDP.10 : Min/Max MVPSD overlap onto the channel (LDMOS_XTOR AND COMP AND POLY2 AND Pplus).") |
| mdp10_l1.forget |
| |
| if CONNECTIVITY_RULES |
| logger.info("CONNECTIVITY_RULES section") |
| |
| connected_mdp_10b, unconnected_mdp_10a = conn_space(mvpsd, 1, 2, euclidian) |
| |
| # Rule MDP.10a: Min MVPSD space within LDMOS_XTOR marking [diff potential]. is 2µm |
| logger.info("Executing rule MDP.10a") |
| mdp10a_l1 = unconnected_mdp_10a |
| mdp10a_l1.output("MDP.10a", "MDP.10a : Min MVPSD space within LDMOS_XTOR marking [diff potential]. : 2µm") |
| mdp10a_l1.forget |
| |
| # Rule MDP.10b: Min MVPSD space [same potential]. Merge if space less than 1um. is 1µm |
| logger.info("Executing rule MDP.10b") |
| mdp10b_l1 = connected_mdp_10b |
| mdp10b_l1.output("MDP.10b", "MDP.10b : Min MVPSD space [same potential]. Merge if space less than 1um. : 1µm") |
| mdp10b_l1.forget |
| |
| else |
| logger.info("CONNECTIVITY_RULES disabled section") |
| |
| # Rule MDP.10a: Min MVPSD space within LDMOS_XTOR marking [diff potential]. is 2µm |
| logger.info("Executing rule MDP.10a") |
| mdp10a_l1 = mvpsd.space(2.um, euclidian).polygons(0.001) |
| mdp10a_l1.output("MDP.10a", "MDP.10a : Min MVPSD space within LDMOS_XTOR marking [diff potential]. : 2µm") |
| mdp10a_l1.forget |
| |
| end #CONNECTIVITY_RULES |
| |
| # Rule MDP.11: Min MVPSD enclosing PCOMP in the drain (MVPSD AND COMP NOT POLY2) direction and in the direction along the transistor width. |
| logger.info("Executing rule MDP.11") |
| mdp11_l1 = mvpsd.edges.not_interacting(pcomp.edges).enclosing(pcomp.edges, 0.8.um, euclidian).polygons(0.001).or(mvpsd.interacting(mvpsd.edges.and(pcomp.edges))) |
| mdp11_l1.output("MDP.11", "MDP.11 : Min MVPSD enclosing PCOMP in the drain (MVPSD AND COMP NOT POLY2) direction and in the direction along the transistor width.") |
| mdp11_l1.forget |
| |
| # Rule MDP.12: Min DNWELL enclose Nplus guard ring (NCOMP). is 0.66µm |
| logger.info("Executing rule MDP.12") |
| mdp12_l1 = dnwell.inside(dualgate).inside(ldmos_xtor).enclosing(ncomp.inside(dualgate).inside(ldmos_xtor), 0.66.um, euclidian).polygons(0.001) |
| mdp12_l2 = ncomp.inside(dualgate).inside(ldmos_xtor).not_outside(dnwell.inside(dualgate).inside(ldmos_xtor)).not(dnwell.inside(dualgate).inside(ldmos_xtor)) |
| mdp12_l = mdp12_l1.or(mdp12_l2) |
| mdp12_l.output("MDP.12", "MDP.12 : Min DNWELL enclose Nplus guard ring (NCOMP). : 0.66µm") |
| mdp12_l1.forget |
| mdp12_l2.forget |
| mdp12_l.forget |
| |
| # rule MDP.13 is not a DRC check |
| |
| # Rule MDP.13a: Max single finger width. is 50µm |
| logger.info("Executing rule MDP.13a") |
| mdp13a_l1 = poly2.and(pcomp).not(mvpsd).inside(dualgate).inside(ldmos_xtor).edges.with_length(50.001.um,nil).extended(0, 0, 0.001, 0.001) |
| mdp13a_l1.output("MDP.13a", "MDP.13a : Max single finger width. : 50µm") |
| mdp13a_l1.forget |
| |
| # Rule MDP.13b: Layout shall have alternative source & drain. |
| logger.info("Executing rule MDP.13b") |
| mdp13b_l1 = ldpmos.not_interacting(mdp_source,1,1).or(ldpmos.not_interacting(mvpsd,1,1)).or(mdp_source.interacting(mvpsd)) |
| mdp13b_l1.output("MDP.13b", "MDP.13b : Layout shall have alternative source & drain.") |
| mdp13b_l1.forget |
| |
| mdp_13c_source_side = ldpmos.interacting(mdp_source.interacting(ldpmos, 2, 2).or(mdp_source.interacting(ncomp.interacting(mdp_source, 2, 2)))) |
| # Rule MDP.13c: Both sides of the transistor shall be terminated by source. |
| logger.info("Executing rule MDP.13c") |
| mdp13c_l1 = mvpsd.covering(pcomp.not_interacting(poly2)).interacting(pcomp, 2, 2).interacting(mdp_13c_source_side) |
| mdp13c_l1.output("MDP.13c", "MDP.13c : Both sides of the transistor shall be terminated by source.") |
| mdp13c_l1.forget |
| |
| mdp_13c_source_side.forget |
| # rule MDP.14 is not a DRC check |
| |
| # Rule MDP.15: Min DNWELL enclosing MVPSD to any DNWELL spacing. is 6µm |
| logger.info("Executing rule MDP.15") |
| mdp15_l1 = dnwell.separation(dnwell.covering(mvpsd).inside(dualgate).inside(ldmos_xtor), 6.um, euclidian).polygons(0.001) |
| mdp15_l1.output("MDP.15", "MDP.15 : Min DNWELL enclosing MVPSD to any DNWELL spacing. : 6µm") |
| mdp15_l1.forget |
| |
| # Rule MDP.16a: Min LDPMOS drain COMP width. is 0.22µm |
| logger.info("Executing rule MDP.16a") |
| mdp16a_l1 = comp.inside(mvpsd).inside(dualgate).inside(ldmos_xtor).width(0.22.um, euclidian).polygons(0.001) |
| mdp16a_l1.output("MDP.16a", "MDP.16a : Min LDPMOS drain COMP width. : 0.22µm") |
| mdp16a_l1.forget |
| |
| # Rule MDP.16b: Min LDPMOS drain COMP enclose contact. is 0µm |
| logger.info("Executing rule MDP.16b") |
| mdp16b_l1 = contact.interacting(pcomp.inside(mvpsd).inside(dualgate).inside(ldmos_xtor)).not_inside(pcomp.inside(mvpsd)) |
| mdp16b_l1.output("MDP.16b", "MDP.16b : Min LDPMOS drain COMP enclose contact. : 0µm") |
| mdp16b_l1.forget |
| |
| mdp17_a1 = mvpsd.inside(dnwell).inside(ldmos_xtor) |
| mdp17_a2 = ncomp.outside(dnwell).outside(nwell) |
| # Rule MDP.17a: For better latch up immunity, it is necessary to put DNWELL guard ring between MVPSD Inside DNWELL covered by LDMOS_XTOR and NCOMP (outside DNWELL and outside Nwell) when spacing between them is less than 40um. |
| logger.info("Executing rule MDP.17a") |
| mdp17a_l1 = mdp17_a1.separation(mdp17_a2,transparent,40.um).polygons(0.001).not_interacting(ncomp.and(dnwell).holes) |
| mdp17a_l1.output("MDP.17a", "MDP.17a : For better latch up immunity, it is necessary to put DNWELL guard ring between MVPSD Inside DNWELL covered by LDMOS_XTOR and NCOMP (outside DNWELL and outside Nwell) when spacing between them is less than 40um.") |
| mdp17a_l1.forget |
| |
| mdp17_a1.forget |
| mdp17_a2.forget |
| # Rule MDP.17c: DNWELL guard ring shall have NCOMP tab to be connected to highest potential |
| logger.info("Executing rule MDP.17c") |
| mdp17c_l1 = dnwell.with_holes.not_covering(ncomp) |
| mdp17c_l1.output("MDP.17c", "MDP.17c : DNWELL guard ring shall have NCOMP tab to be connected to highest potential") |
| mdp17c_l1.forget |
| |
| |
| exec_end_time = Time.now |
| run_time = exec_end_time - exec_start_time |
| logger.info("DRC Run time %f seconds" % [run_time]) |
| |