| ################################################################################################ |
| # 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 |
| |
| #================================================ |
| #--------------------CONTACT--------------------- |
| #================================================ |
| |
| logger.info('Starting CONTACT derivations') |
| |
| main_contact = contact.not(sramcore) |
| |
| # Rule CO.1: Min/max contact size. is 0.22µm |
| logger.info('Executing rule CO.1') |
| co1_l1 = contact.edges.without_length(0.22.um).extended(0, 0, 0.001, 0.001) |
| co1_l1.output('CO.1', 'CO.1 : Min/max contact size. : 0.22µm') |
| co1_l1.forget |
| |
| # Rule CO.2a: min. contact spacing is 0.25µm |
| logger.info('Executing rule CO.2a') |
| co2a_l1 = contact.space(0.25.um, euclidian).polygons(0.001) |
| co2a_l1.output('CO.2a', 'CO.2a : min. contact spacing : 0.25µm') |
| co2a_l1.forget |
| |
| # Rule CO.2b: Space in 4x4 or larger contact array. is 0.28µm |
| logger.info('Executing rule CO.2b') |
| # Array to be filterd |
| ## array_length = 3 * exact_size + 3 * min_space |
| co2b_egde_length = 0.22 * 3 + 3 * 0.28 |
| poss_4_4_contact = contact.sized(0.16, 'square_limit').merged.sized(-0.16, 'square_limit') |
| co_4x4_all = poss_4_4_contact.with_bbox_min(co2b_egde_length..nil).interacting(contact, 16..nil) |
| co_4x4_loc_exc = co_4x4_all.width(co2b_egde_length, |
| projection_limits(co2b_egde_length..1000 * co2b_egde_length)).polygons |
| co_4x4_loc = co_4x4_all.not_interacting(co_4x4_loc_exc) |
| selected_co = contact.interacting(co_4x4_loc) |
| co2b_l1 = selected_co.space(0.28.um, euclidian) |
| co2b_l1.output('CO.2b', 'CO.2b : Space in 4x4 or larger contact array. : 0.28µm') |
| poss_4_4_contact.forget |
| co_4x4_all.forget |
| co_4x4_loc_exc.forget |
| selected_co.forget |
| co2b_l1.forget |
| |
| # Rule CO.2c : Max. Space for well tap guard ring contacts for High Voltage LDMOS is 11µm |
| hv_ring = ntap.join(ptap).and(ldmos_xtor) |
| hv_ring_con = contact.and(hv_ring) |
| co2c_l1 = hv_ring_con.not_interacting(hv_ring_con.space(11.um).polygons(0.001)) |
| co2c_l1.output('CO.2c','CO.2c : Max. Space for well tap guard ring contacts for High Voltage LDMOS is 11µm') |
| co2c_l1.forget |
| hv_ring.forget |
| |
| # Rule CO.3: Poly2 overlap of contact. is 0.1µm |
| logger.info('Executing rule CO.3') |
| co3_l1 = poly2.enclosing(contact.outside(sramcore), 0.1.um, euclidian).polygons(0.001) |
| co3_l2 = contact.outside(sramcore).not_outside(poly2).not(poly2) |
| co3_l = co3_l1.or(co3_l2) |
| co3_l.output('CO.3', 'CO.3 : Poly2 overlap of contact. : 0.1µm') |
| co3_l1.forget |
| co3_l2.forget |
| co3_l.forget |
| |
| # Rule CO.4: COMP overlap of contact. is 0.1µm |
| logger.info('Executing rule CO.4') |
| co4_l1 = comp.not(mvsd).not(mvpsd).enclosing(contact.outside(sramcore), 0.1.um, euclidian).polygons(0.001) |
| co4_l2 = contact.outside(sramcore).not_outside(comp.not(mvsd).not(mvpsd)).not(comp.not(mvsd).not(mvpsd)) |
| co4_l = co4_l1.or(co4_l2) |
| co4_l.output('CO.4', 'CO.4 : COMP overlap of contact. : 0.1µm') |
| co4_l1.forget |
| co4_l2.forget |
| co4_l.forget |
| |
| |
| # Rule CO.6a: (i) Metal1 (< 0.34um) end-of-line overlap. is 0.06µm. |
| ## (Applies to all < 0.34µm wide metal lines, |
| ## excluding metal branches shorter than 0.24µm.) |
| logger.info('Executing rule CO.6a') |
| cont_6a_cond = metal1.width(0.34.um + 1.dbu).with_length(0.24.um, nil, both) |
| cont_6a_cond_edge1 = cont_6a_cond.first_edges |
| cont_6a_cond_edge2 = cont_6a_cond.second_edges |
| cont_6a_eol = metal1.edges.with_length(nil, 0.34.um).interacting(cont_6a_cond_edge1).interacting(cont_6a_cond_edge2) |
| .not(cont_6a_cond_edge1).not(cont_6a_cond_edge2) |
| cont_6a_l1 = contact.edges.enclosed(cont_6a_eol, 0.06.um, projection) |
| cont_6a_l1.output('CO.6a', 'CO.6a : (i) Metal1 (< 0.34um) end-of-line overlap contact |
| (Applies to all < 0.34µm wide metal lines, |
| excluding metal branches shorter than 0.24µm) : 0.06µm') |
| cont_6a_l1.forget |
| cont_6a_cond.forget |
| cont_6a_eol.forget |
| cont_6a_cond.forget |
| cont_6a_cond_edge1.forget |
| cont_6a_cond_edge2.forget |
| |
| # Rule CO.6b: Min. Metal1 enclose Contact by sides (a, b, c, d) [Outside Not Allowed] are (0.005,0.06,0.005,0.06) or (0,04,0.04,0.04,0.04) |
| logger.info('Executing rule CO.6b') |
| co6b_l1 = contact.enclosed(metal1, 0.04.um).polygons(0.001) |
| co6b_l2 = contact.enclosed(metal1, 0.06.um).polygons(0.001) |
| co6b_l3 = contact.enclosed(metal1, 0.005.um).polygons(0.001) |
| co6b_l = co6b_l1.join(co6b_l2.interacting(co6b_l3)) |
| co6b_l.output('CO.6b','CO.6b : Min. Metal1 enclose Contact by sides (a, b, c, d) [Outside Not Allowed] are (0.005,0.06,0.005,0.06) or (0,04,0.04,0.04,0.04)') |
| co6b_l1.forget |
| co6b_l2.forget |
| co6b_l3.forget |
| co6b_l.forget |
| |
| # rule CO.6c is not a DRC check |
| |
| # Rule CO.7a : Space from COMP contact to Poly2 on COMP. is 0.16µm |
| logger.info('Executing rule CO.7a') |
| co7a_l1 = contact.not_outside(comp).separation(tgate, 0.16.um, euclidian).polygons(0.001) |
| co7a_l1.output('CO.7a', 'CO.7a : Space from COMP contact to Poly2 on COMP. : 0.16µm') |
| co7a_l1.forget |
| |
| # Rule CO.7b : Min. COMP contact space to Poly2 on COMP for drain side of asymmetric HVNMOS/HVPMOS is 0.7/0.6 um |
| logger.info('Executing rule CO.7b') |
| co7b_l1 = contact.not_outside(comp.interacting(hvnddd)).separation(tgate.interacting(hvnddd), 0.7.um, euclidian).polygons(0.001) |
| co7b_l2 = contact.not_outside(comp.interacting(hvpddd)).separation(tgate.interacting(hvpddd), 0.6.um, euclidian).polygons(0.001) |
| co7b_l = co7b_l1.or(co7b_l2) |
| co7b_l.output('CO.7b', 'CO.7b : Space from COMP contact to Poly2 on COMP.Min. COMP contact space to Poly2 on COMP for drain side of asymmetric HVNMOS/HVPMOS: 0.7/0.6 um') |
| co7b_l.forget |
| co7b_l1.forget |
| co7b_l2.forget |
| |
| # Rule CO.7c : Min. COMP contact space to Poly2 on COMP for source side of asymmetric HVNMOS and HVPMOS is 0.16 um |
| logger.info('Executing rule CO.7c') |
| co7c_l1 = contact.not_outside(comp.not(hvnddd.or(hvpddd))).separation(tgate.interacting(hvnddd.or(hvpddd)), 0.16.um, euclidian).polygons(0.001) |
| co7c_l1.output('CO.7c', 'CO.7c : Min. COMP contact space to Poly2 on COMP for source side of asymmetric HVNMOS and HVPMOS : 0.16µm') |
| co7c_l1.forget |
| |
| # Rule CO.8: Space from Poly2 contact to COMP. is 0.2µm |
| logger.info('Executing rule CO.8') |
| co8_l1 = contact.not_outside(poly2).separation(comp, 0.2.um, euclidian).polygons(0.001) |
| co8_l1.output('CO.8', 'CO.8 : Space from Poly2 contact to COMP. : 0.2µm') |
| co8_l1.forget |
| |
| # Rule CO.10: Contact on Poly2 gate over COMP is forbidden. |
| logger.info('Executing rule CO.10') |
| co10_l1 = contact.not_outside(tgate) |
| co10_l1.output('CO.10', 'CO.10 : Contact on Poly2 gate over COMP is forbidden.') |
| co10_l1.forget |
| |
| # Rule CO.11: Contact on field oxide is forbidden. |
| logger.info('Executing rule CO.11') |
| co11_l1 = contact.not_inside(comp.or(poly2)) |
| co11_l1.output('CO.11', 'CO.11 : Contact on field oxide is forbidden.') |
| co11_l1.forget |
| |
| n_source = (ncomp.not(poly2).interacting(pcomp).interacting(poly2)) |
| p_source = (pcomp.not(poly2).interacting(ncomp).interacting(poly2)) |
| p_tap = (pcomp.not_interacting(poly2).interacting(ncomp)) |
| n_tap = (ncomp.not_interacting(poly2).interacting(pcomp)) |
| |
| # Rule CO.12: Butted source NCOMP overlap with contact is 0.16 µm |
| logger.info('Executing rule CO.12') |
| co12_l1 = n_source.overlap(contact,0.16.um).polygons(0.001) |
| co12_l1.output('CO.12','CO.12: Butted source NCOMP overlap with contact is 0.16 µm') |
| co12_l1.forget |
| |
| # Rule CO.13 : Butted source PCOMP overlap with contact is 0.16 µm |
| co13_l1 = p_source.overlap(contact,0.16.um).polygons(0.001) |
| co13_l1.output('CO.13','CO.13 : Butted source PCOMP overlap with contact is 0.16 µm') |
| co13_l1.forget |
| |
| # Rule CO.14a : Min Pbody width, with contact is 0.42 µm |
| co14a_l1 = p_tap.interacting(contact).width(0.42.um).polygons(0.001) |
| co14a_l1.output('CO.14a',' CO.14a : Min Pbody width, with contact is 0.42 µm') |
| co14a_l1.forget |
| |
| # Rule CO.14b : There must be at least one row of contact inside P-Body |
| co14b_l1 = p_tap.not_interacting(contact.inside(p_tap)) |
| co14b_l1.output('CO.14b','CO.14b : There must be at least one row of contact inside P-Body') |
| co14b_l1.forget |
| |
| # Rule CO.14c : Min Pbody contact enclosure by butted PCOMP is 0.1 µm |
| co14c_l1 = p_tap.enclosing(contact.inside(p_tap),0.1.um).polygons(0.001) |
| co14c_l1.output('CO.14c','CO.14c : Min Pbody contact enclosure by butted PCOMP is 0.1 µm') |
| co14c_l1.forget |
| |
| # Rule CO.15a : Min Nbody width, with contact is 0.42 µm |
| co15a_l1 = n_tap.interacting(contact).width(0.42.um).polygons(0.001) |
| co15a_l1.output('CO.15a','CO.15a : Min Nbody width, with contact is 0.42 µm') |
| co15a_l1.forget |
| |
| # Rule CO.15b : There must be at least one row of contact inside N-Body |
| co15b_l1 = n_tap.not_interacting(contact.inside(n_tap)) |
| co15b_l1.output('CO.15b','CO.15b : There must be at least one row of contact inside N-Body') |
| co15b_l1.forget |
| |
| # Rule CO.15c : Min Nbody contact enclosure by butted NCOMP is 0.1 µm |
| co15c_l1 = n_tap.enclosing(contact.inside(n_tap),0.1.um).polygons(0.001) |
| co15c_l1.output('CO.15c','CO.15c : Min Nbody contact enclosure by butted NCOMP is 0.1 µm') |
| co15c_l1.forget |
| |
| end #FEOL |