| ################################################################################################ |
| # 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.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.enclosing(contact.outside(sramcore), 0.1.um, euclidian).polygons(0.001) |
| co4_l2 = contact.outside(sramcore).not_outside(comp).not(comp) |
| 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 |
| |
| co_5a_ncomp_butted = ncomp.not(pplus).interacting(pcomp.not(nplus)).not_overlapping(pcomp.not(nplus)) |
| # Rule CO.5a: Nplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). is 0.1µm |
| logger.info('Executing rule CO.5a') |
| co5a_l1 = co_5a_ncomp_butted.enclosing(contact, 0.1.um, euclidian).polygons(0.001) |
| co5a_l2 = contact.not_outside(co_5a_ncomp_butted).not(co_5a_ncomp_butted) |
| co5a_l = co5a_l1.or(co5a_l2) |
| co5a_l.output('CO.5a', 'CO.5a : Nplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). : 0.1µm') |
| co5a_l1.forget |
| co5a_l2.forget |
| co5a_l.forget |
| |
| co_5a_ncomp_butted.forget |
| |
| co_5b_pcomp_butted = pcomp.not(nplus).interacting(ncomp.not(pplus)).not_overlapping(ncomp.not(pplus)) |
| # Rule CO.5b: Pplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). is 0.1µm |
| logger.info('Executing rule CO.5b') |
| co5b_l1 = co_5b_pcomp_butted.enclosing(contact, 0.1.um, euclidian).polygons(0.001) |
| co5b_l2 = contact.not_outside(co_5b_pcomp_butted).not(co_5b_pcomp_butted) |
| co5b_l = co5b_l1.or(co5b_l2) |
| co5b_l.output('CO.5b', 'CO.5b : Pplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). : 0.1µm') |
| co5b_l1.forget |
| co5b_l2.forget |
| co5b_l.forget |
| |
| co_5b_pcomp_butted.forget |
| |
| # Rule CO.6: Metal1 overlap of contact. |
| logger.info('Executing rule CO.6') |
| co6_l1 = metal1.enclosing(contact, 0.005.um, euclidian).polygons(0.001).or(contact.not(metal1)) |
| co6_l1.output('CO.6', 'CO.6 : Metal1 overlap of contact.') |
| co6_l1.forget |
| |
| cop6a_cond = metal1.drc( width <= 0.34.um).with_length(0.24.um,nil,both) |
| cop6a_eol = metal1.edges.with_length(nil, 0.34.um).interacting(cop6a_cond.first_edges).interacting(cop6a_cond.second_edges).not(cop6a_cond.first_edges).not(cop6a_cond.second_edges) |
| # Rule CO.6a: (i) Metal1 (< 0.34um) end-of-line overlap. is 0.06µm |
| logger.info('Executing rule CO.6a') |
| co6a_l1 = cop6a_eol.enclosing(contact.edges,0.06.um, projection).polygons(0.001) |
| co6a_l1.output('CO.6a', 'CO.6a : (i) Metal1 (< 0.34um) end-of-line overlap. : 0.06µm') |
| co6a_l1.forget |
| |
| cop6a_cond.forget |
| |
| cop6a_eol.forget |
| |
| co_6b_1 = contact.edges.interacting(contact.drc(enclosed(metal1, projection) < 0.04.um).edges.centers(0, 0.5)) |
| co_6b_2 = contact.edges.interacting(contact.drc(0.04.um <= enclosed(metal1, projection) < 0.06.um).centers(0, 0.5)) |
| co_6b_3 = co_6b_1.extended(0, 0, 0, 0.001, joined).corners(90) |
| # Rule CO.6b: (ii) If Metal1 overlaps contact by < 0.04um on one side, adjacent metal1 edges overlap is 0.06µm |
| logger.info('Executing rule CO.6b') |
| co6b_l1 = co_6b_2.not_in(co_6b_1).interacting(co_6b_1).or(co_6b_1.interacting(co_6b_3)).not(sramcore).enclosed(metal1.outside(sramcore).edges, 0.06.um).polygons(0.001) |
| co6b_l1.output('CO.6b', 'CO.6b : (ii) If Metal1 overlaps contact by < 0.04um on one side, adjacent metal1 edges overlap : 0.06µm') |
| co6b_l1.forget |
| |
| co_6b_1.forget |
| |
| co_6b_2.forget |
| |
| co_6b_3.forget |
| |
| # rule CO.6c is not a DRC check |
| |
| # Rule CO.7: Space from COMP contact to Poly2 on COMP. is 0.16µm |
| logger.info('Executing rule CO.7') |
| co7_l1 = contact.not_outside(comp).not(otp_mk).separation(tgate.not(otp_mk), 0.16.um, euclidian).polygons(0.001) |
| co7_l1.output('CO.7', 'CO.7 : Space from COMP contact to Poly2 on COMP. : 0.16µm') |
| co7_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.9: Contact on NCOMP to PCOMP butting edge is forbidden (contact must not straddle butting edge). |
| logger.info('Executing rule CO.9') |
| co9_l1 = contact.interacting(ncomp.edges.and(pcomp.edges)) |
| co9_l1.output('CO.9', 'CO.9 : Contact on NCOMP to PCOMP butting edge is forbidden (contact must not straddle butting edge).') |
| co9_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(comp.or(poly2)) |
| co11_l1.output('CO.11', 'CO.11 : Contact on field oxide is forbidden.') |
| co11_l1.forget |
| |
| end #FEOL |