| # DRC for SKY130 according to : |
| # https://skywater-pdk.readthedocs.io/en/latest/rules/periphery.html |
| # https://skywater-pdk.readthedocs.io/en/latest/rules/layers.html |
| # |
| # Distributed under GNU GPLv3: https://www.gnu.org/licenses/ |
| # |
| # History : |
| # 2020-10-04 : v1.0 : initial release |
| # |
| ########################################################################################## |
| |
| # optionnal for a batch launch : klayout -b -rd input=my_layout.gds -rd report=sky130_drc.txt -r drc_sky130.drc |
| if $input |
| source($input, $top_cell) |
| end |
| |
| if $report |
| report("SKY130 DRC runset", $report) |
| else |
| report("SKY130 DRC runset", File.join(File.dirname(RBA::CellView::active.filename), "sky130_drc.txt")) |
| end |
| |
| AL = true # do not change |
| CU = false # do not change |
| # choose betwen only one of AL or CU back-end flow here : |
| backend_flow = AL |
| |
| # enable / disable rule groups |
| if $feol == "0" |
| FEOL = false # front-end-of-line checks |
| else |
| FEOL = true # front-end-of-line checks |
| end |
| if $beol == "0" |
| BEOL = false # back-end-of-line checks |
| else |
| BEOL = true # back-end-of-line checks |
| end |
| if $offgrid == "0" |
| OFFGRID = false # manufacturing grid/angle checks |
| else |
| OFFGRID = true # manufacturing grid/angle checks |
| end |
| |
| # klayout setup |
| ######################## |
| # use a tile size of 1mm - not used in deep mode- |
| # tiles(1000.um) |
| # use a tile border of 10 micron: |
| # tile_borders(1.um) |
| #no_borders |
| |
| # hierachical |
| deep |
| |
| if $thr |
| threads($thr) |
| else |
| threads(4) |
| end |
| |
| # if more inof is needed, set true |
| # verbose(true) |
| verbose(true) |
| |
| # layers definitions |
| ######################## |
| |
| # all except purpose (datatype) 5 -- label and 44 -- via |
| li_wildcard = "67/20" |
| mcon_wildcard = "67/44" |
| |
| m1_wildcard = "68/20" |
| via_wildcard = "68/44" |
| |
| m2_wildcard = "69/20" |
| via2_wildcard = "69/44" |
| |
| m3_wildcard = "70/20" |
| via3_wildcard = "70/44" |
| |
| m4_wildcard = "71/20" |
| via4_wildcard = "71/44" |
| |
| m5_wildcard = "72/20" |
| |
| diff = input(65, 20) |
| tap = polygons(65, 44) |
| nwell = polygons(64, 20) |
| dnwell = polygons(64, 18) |
| pwbm = polygons(19, 44) |
| pwde = polygons(124, 20) |
| natfet = polygons(124, 21) |
| hvtr = polygons(18, 20) |
| hvtp = polygons(78, 44) |
| ldntm = polygons(11, 44) |
| hvi = polygons(75, 20) |
| tunm = polygons(80, 20) |
| lvtn = polygons(125, 44) |
| poly = polygons(66, 20) |
| hvntm = polygons(125, 20) |
| nsdm = polygons(93, 44) |
| psdm = polygons(94, 20) |
| rpm = polygons(86, 20) |
| urpm = polygons(79, 20) |
| npc = polygons(95, 20) |
| licon = polygons(66, 44) |
| |
| li = polygons(li_wildcard) |
| mcon = polygons(mcon_wildcard) |
| |
| m1 = polygons(m1_wildcard) |
| via = polygons(via_wildcard) |
| |
| m2 = polygons(m2_wildcard) |
| via2 = polygons(via2_wildcard) |
| |
| m3 = polygons(m3_wildcard) |
| via3 = polygons(via3_wildcard) |
| |
| m4 = polygons(m4_wildcard) |
| via4 = polygons(via4_wildcard) |
| |
| m5 = polygons(m5_wildcard) |
| |
| pad = polygons(76, 20) |
| nsm = polygons(61, 20) |
| capm = polygons(89, 44) |
| cap2m = polygons(97, 44) |
| vhvi = polygons(74, 21) |
| uhvi = polygons(74, 22) |
| npn = polygons(82, 20) |
| inductor = polygons(82, 24) |
| vpp = polygons(82, 64) |
| pnp = polygons(82, 44) |
| lvs_prune = polygons(84, 44) |
| ncm = polygons(92, 44) |
| padcenter = polygons(81, 20) |
| mf = polygons(76, 44) |
| areaid_sl = polygons(81, 1) |
| areaid_ce = polygons(81, 2) |
| areaid_fe = polygons(81, 3) |
| areaid_sc = polygons(81, 4) |
| areaid_sf = polygons(81, 6) |
| areaid_sw = polygons(81, 7) |
| areaid_sr = polygons(81, 8) |
| areaid_mt = polygons(81, 10) |
| areaid_dt = polygons(81, 11) |
| areaid_ft = polygons(81, 12) |
| areaid_ww = polygons(81, 13) |
| areaid_ld = polygons(81, 14) |
| areaid_ns = polygons(81, 15) |
| areaid_ij = polygons(81, 17) |
| areaid_zr = polygons(81, 18) |
| areaid_ed = polygons(81, 19) |
| areaid_de = polygons(81, 23) |
| areaid_rd = polygons(81, 24) |
| areaid_dn = polygons(81, 50) |
| areaid_cr = polygons(81, 51) |
| areaid_cd = polygons(81, 52) |
| areaid_st = polygons(81, 53) |
| areaid_op = polygons(81, 54) |
| areaid_en = polygons(81, 57) |
| areaid_en20 = polygons(81, 58) |
| areaid_le = polygons(81, 60) |
| areaid_hl = polygons(81, 63) |
| areaid_sd = polygons(81, 70) |
| areaid_po = polygons(81, 81) |
| areaid_it = polygons(81, 84) |
| areaid_et = polygons(81, 101) |
| areaid_lvt = polygons(81, 108) |
| areaid_re = polygons(81, 125) |
| areaid_ag = polygons(81, 79) |
| poly_rs = polygons(66, 13) |
| diff_rs = polygons(65, 13) |
| pwell_rs = polygons(64, 13) |
| li_rs = polygons(67, 13) |
| cfom = polygons(22, 20) |
| |
| |
| # Define a new custom function that selects polygons by their number of holes: |
| # It will return a new layer containing those polygons with min to max holes. |
| # max can be nil to omit the upper limit. |
| class DRC::DRCLayer |
| def with_holes(min, max) |
| new_data = RBA::Region::new |
| self.data.each do |p| |
| if p.holes >= (min || 0) && (!max || p.holes <= max) |
| new_data.insert(p) |
| end |
| end |
| DRC::DRCLayer::new(@engine, new_data) |
| end |
| end |
| |
| # DRC section |
| ######################## |
| log("DRC section") |
| |
| if FEOL |
| log("FEOL section") |
| # dnwell |
| log("START: 64/18 (dnwell)") |
| dnwell.width(3.0, euclidian).output("dnwell.2", "dnwell.2 : min. dnwell width : 3.0um") |
| log("END: 64/18 (dnwell)") |
| |
| # nwell |
| log("START: 64/20 (nwell)") |
| nwell.width(0.84, euclidian).output("nwell.1", "nwell.1 : min. nwell width : 0.84um") |
| nwell.space(1.27, euclidian).output("nwell.2a", "nwell.2a : min. nwell spacing (merged if less) : 1.27um") |
| log("END: 64/20 (nwell)") |
| |
| # hvtp |
| log("START: 78/44 (hvtp)") |
| hvtp.width(0.38, euclidian).output("hvtp.1", "hvtp.1 : min. hvtp width : 0.38um") |
| hvtp.space(0.38, euclidian).output("hvtp.2", "hvtp.2 : min. hvtp spacing : 0.38um") |
| log("END: 78/44 (hvtp)") |
| |
| # hvtr |
| log("START: 18/20 (htvr)") |
| hvtr.width(0.38, euclidian).output("hvtr.1", "hvtr.1 : min. hvtr width : 0.38um") |
| hvtr.separation(hvtp, 0.38, euclidian).output("hvtr.2", "hvtr.2 : min. hvtr spacing : 0.38um") |
| hvtr.and(hvtp).output("hvtr.2_a", "hvtr.2_a : hvtr must not overlap hvtp") |
| log("END: 18/20 (htvr)") |
| |
| # lvtn |
| log("START: 25/44 (lvtn)") |
| lvtn.width(0.38, euclidian).output("lvtn.1a", "lvtn.1a : min. lvtn width : 0.38um") |
| lvtn.space(0.38, euclidian).output("lvtn.2", "lvtn.2 : min. lvtn spacing : 0.38um") |
| log("END: 25/44 (lvtn)") |
| |
| # ncm |
| log("START: 92/44 (ncm)") |
| ncm.width(0.38, euclidian).output("ncm.1", "ncm.1 : min. ncm width : 0.38um") |
| ncm.space(0.38, euclidian).output("ncm.2a", "ncm.2a : min. ncm spacing : 0.38um") |
| log("END: 92/44 (ncm)") |
| |
| # diff-tap |
| log("START: 65/20 (diff)") |
| difftap = diff.or(tap) |
| diff_width = diff.rectangles.width(0.15, euclidian).polygons |
| diff_cross_areaid_ce = diff_width.edges.outside_part(areaid_ce).not(diff_width.outside(areaid_ce).edges) |
| diff_cross_areaid_ce.output("difftap.1", "difftap.1 : min. diff width across areaid:ce : 0.15um") |
| diff.not(areaid_ce).width(0.15, euclidian).output("difftap.1_a", "difftap.1_a : min. diff width in periphery : 0.15um") |
| log("END: 65/20 (diff)") |
| |
| log("START: 65/44 (tap)") |
| tap_width = tap.rectangles.width(0.15, euclidian).polygons |
| tap_cross_areaid_ce = tap_width.edges.outside_part(areaid_ce).not(tap_width.outside(areaid_ce).edges) |
| tap_cross_areaid_ce.output("difftap.1_b", "difftap.1_b : min. tap width across areaid:ce : 0.15um") |
| tap.not(areaid_ce).width(0.15, euclidian).output("difftap.1_c", "difftap.1_c : min. tap width in periphery : 0.15um") |
| log("END: 65/44 (tap)") |
| |
| difftap.space(0.27, euclidian).output("difftap.3", "difftap.3 : min. difftap spacing : 0.27um") |
| |
| # tunm |
| log("START: 80/20 (tunm)") |
| tunm.width(0.41, euclidian).output("tunm.1", "tunm.1 : min. tunm width : 0.41um") |
| tunm.space(0.5, euclidian).output("tunm.2", "tunm.2 : min. tunm spacing : 0.5um") |
| log("END: 80/20 (tunm)") |
| |
| # poly |
| log("START: 66/20 (poly)") |
| poly.width(0.15, euclidian).output("poly.1a", "poly.1a : min. poly width : 0.15um") |
| poly.not(areaid_ce).space(0.21, euclidian).output("poly.2", "poly.2 : min. poly spacing : 0.21um") |
| |
| |
| # rpm |
| log("START: 86/20 (rpm)") |
| rpm.width(1.27, euclidian).output("rpm.1a", "rpm.1a : min. rpm width : 1.27um") |
| rpm.space(0.84, euclidian).output("rpm.2", "rpm.2 : min. rpm spacing : 0.84um") |
| log("END: 86/20 (rpm)") |
| |
| # urpm |
| log("START: 79/20 (urpm)") |
| urpm.width(1.27, euclidian).output("urpm.1a", "urpm.1a : min. rpm width : 1.27um") |
| urpm.space(0.84, euclidian).output("urpm.2", "urpm.2 : min. rpm spacing : 0.84um") |
| log("END: 79/20 (urpm)") |
| |
| # npc |
| log("START: 95/20 (npc)") |
| npc.width(0.27, euclidian).output("npc.1", "npc.1 : min. npc width : 0.27um") |
| npc.space(0.27, euclidian).output("npc.2", "npc.2 : min. npc spacing, should be mnually merge if less : 0.27um") |
| log("END: 95/20 (npc)") |
| |
| # licon |
| log("START: 66/44 (licon)") |
| ringLICON = licon.drc(with_holes > 0) |
| rectLICON = licon.not(ringLICON) |
| xfom = difftap.not(poly) |
| licon1ToXfom = licon.interacting(licon.and(xfom)) |
| licon1ToXfom_PERI = licon1ToXfom.not(areaid_ce) |
| rectLICON.non_rectangles.output("licon.1", "licon.1 : licon should be rectangle") |
| rectLICON.not(rpm.or(urpm)).edges.without_length(0.17).output("licon.1_a/b", "licon.1_a/b : minimum/maximum width of licon : 0.17um") |
| licon1ToXfom_PERI.separation(npc, 0.09, euclidian).output("licon.13", "licon.13 : min. difftap licon spacing to npc : 0.09um") |
| licon1ToXfom_PERI.and(npc).output("licon.13_a", "licon.13_a : licon of diffTap in periphery must not overlap npc") |
| licon.interacting(poly).and(licon.interacting(difftap)).output("licon.17", "licon.17 : Licons may not overlap both poly and (diff or tap)") |
| log("END: 66/44 (licon)") |
| |
| # CAPM |
| log("START: 89/44 (capm)") |
| m3_bot_plate = (capm.and(m3)).sized(0.14) |
| capm.width(1.0, euclidian).output("capm.1", "capm.1 : min. capm width : 1.0um") |
| capm.space(0.84, euclidian).output("capm.2a", "capm.2a : min. capm spacing : 0.84um") |
| m3.interacting(capm).isolated(1.2, euclidian).output("capm.2b", "capm.2b : min. capm spacing : 1.2um") |
| m3_bot_plate.isolated(1.2, euclidian).output("capm.2b_a", "capm.2b_a : min. spacing of m3_bot_plate : 1.2um") |
| capm.and(m3).enclosing(m3, 0.14, euclidian).output("capm.3", "capm.3 : min. capm and m3 enclosure of m3 : 0.14um") |
| m3.enclosing(capm, 0.14, euclidian).output("capm.3_a", "capm.3_a : min. m3 enclosure of capm : 0.14um") |
| capm.enclosing(via3, 0.14, euclidian).output("capm.4", "capm.4 : min. capm enclosure of via3 : 0.14um") |
| capm.separation(via3, 0.14, euclidian).output("capm.5", "capm.5 : min. capm spacing to via3 : 0.14um") |
| log("END: 89/44 (capm)") |
| |
| # CAP2M |
| log("START: 97/44 (cap2m)") |
| m4_bot_plate = (cap2m.and(m4)).sized(0.14) |
| cap2m.width(1.0, euclidian).output("cap2m.1", "cap2m.1 : min. cap2m width : 1.0um") |
| cap2m.space(0.84, euclidian).output("cap2m.2a", "cap2m.2a : min. cap2m spacing : 0.84um") |
| m4.interacting(cap2m).isolated(1.2, euclidian).output("cap2m.2b", "cap2m.2b : min. cap2m spacing : 1.2um") |
| # This rule has false positive errors |
| m4_bot_plate.isolated(1.2, euclidian).output("cap2m.2b_a", "cap2m.2b_a : min. spacing of m4_bot_plate : 1.2um") |
| cap2m.and(m4).enclosing(m4, 0.14, euclidian).output("cap2m.3", "cap2m.3 : min. m4 enclosure of cap2m : 0.14um") |
| m4.enclosing(cap2m, 0.14, euclidian).output("cap2m.3_a", "cap2m.3_a : min. m4 enclosure of cap2m : 0.14um") |
| cap2m.enclosing(via4, 0.2, euclidian).output("cap2m.4", "cap2m.4 : min. cap2m enclosure of via4 : 0.14um") |
| cap2m.separation(via4, 0.2, euclidian).output("cap2m.5", "cap2m.5 : min. cap2m spacing to via4 : 0.14um") |
| log("END: 97/44 (cap2m)") |
| end #FEOL |
| |
| if BEOL |
| log("BEOL section") |
| |
| # li |
| log("START: 67/20 (li)") |
| linotace = li.not(areaid_ce) |
| linotace.width(0.17, euclidian).output("li.1", "li.1 : min. li width : 0.17um") |
| # This rule is taking a long time in some slots |
| linotace.space(0.17, euclidian).output("li.3", "li.3 : min. li spacing : 0.17um") |
| licon_peri = licon.not(areaid_ce) |
| li_edges_with_less_enclosure = li.enclosing(licon_peri, 0.08, projection).second_edges |
| error_corners = li_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu) |
| li_interact = licon_peri.interacting(error_corners.polygons(1.dbu)) |
| li_interact.output("li.5", "li.5 : min. li enclosure of licon of 2 adjacent edges : 0.08um") |
| li.with_area(nil, 0.0561).output("li.6", "li.6 : min. li area : 0.0561um²") |
| log("END: 67/20 (li)") |
| |
| # ct |
| log("START: 67/44 (mcon)") |
| mconnotace = mcon.not(areaid_ce) |
| ringMCON = mcon.drc(with_holes > 0) |
| rectMCON = mcon.not(ringMCON) |
| rectMCON_peri = rectMCON.not(areaid_ce) |
| rectMCON.non_rectangles.output("ct.1", "ct.1: non-ring mcon should be rectangular") |
| # rectMCON_peri.edges.without_length(0.17).output("ct.1_a/b", "ct.1_a/b : minimum/maximum width of mcon : 0.17um") |
| rectMCON_peri.drc(width < 0.17).output("ct.1_a", "ct.1_a : minimum width of mcon : 0.17um") |
| rectMCON_peri.drc(length > 0.17).output("ct.1_b", "ct.1_b : maximum length of mcon : 0.17um") |
| mcon.space(0.19, euclidian).output("ct.2", "ct.2 : min. mcon spacing : 0.19um") |
| ringMCON.width(0.17, euclidian).output("ct.3", "ct.3 : min. width of ring-shaped mcon : 0.17um") |
| ringMCON.drc(width >= 0.175).output("ct.3_a", "ct.3_a : max. width of ring-shaped mcon : 0.175um") |
| ringMCON.not(areaid_sl).output("ct.3_b", "ct.3_b: ring-shaped mcon must be enclosed by areaid_sl") |
| mconnotace.not(li).output("ct.4", "ct.4 : mcon should covered by li") |
| log("END: 67/44 (mcon)") |
| |
| # m1 |
| log("START: 68/20 (m1)") |
| m1.width(0.14, euclidian).output("m1.1", "m1.1 : min. m1 width : 0.14um") |
| huge_m1 = m1.sized(-1.5).sized(1.5).snap(0.005) & m1 |
| non_huge_m1 = m1.edges - huge_m1 |
| huge_m1 = huge_m1.edges.outside_part(m1.merged) |
| |
| non_huge_m1.space(0.14, euclidian).output("m1.2", "m1.2 : min. m1 spacing : 0.14um") |
| |
| (huge_m1.separation(non_huge_m1, 0.28, euclidian) + huge_m1.space(0.28, euclidian)).output("m1.3ab", "m1.3ab : min. 3um.m1 spacing m1 : 0.28um") |
| |
| #not_in_cell6 = layout(source.cell_obj).select("-s8cell_ee_plus_sseln_a", "-s8cell_ee_plus_sseln_b", "-s8cell_ee_plus_sselp_a", "-s8cell_ee_plus_sselp_b", "-s8fpls_pl8", "-s8fs_cmux4_fm") |
| not_in_cell6 = layout(source.cell_obj).select("-s8cell_ee_plus_sseln_a", "-s8cell_ee_plus_sseln_b", "-s8cell_ee_plus_sselp_a", "-s8cell_ee_plus_sselp_b", "-s8fs_cmux4_fm") |
| not_in_cell6_m1 = not_in_cell6.input(m1_wildcard) |
| |
| not_in_cell6_m1.enclosing(mconnotace, 0.03, euclidian).output("791_m1.4", "791_m1.4 : min. m1 enclosure of mcon : 0.03um") |
| mconnotace.not(m1).output("m1.4", "m1.4 : mcon periphery must be enclosed by m1") |
| in_cell6 = layout(source.cell_obj).select("-*", "+s8cell_ee_plus_sseln_a", "+s8cell_ee_plus_sseln_b", "+s8cell_ee_plus_sselp_a", "+s8cell_ee_plus_sselp_b", "+s8fpls_pl8", "+s8fs_cmux4_fm") |
| in_cell6_m1 = in_cell6.input(m1_wildcard) |
| in_cell6_m1.enclosing(mcon, 0.005, euclidian).output("m1.4a", "m1.4a : min. m1 enclosure of mcon for specific cells : 0.005um") |
| |
| in_cell6_m1.not(m1).output('m1.4a_a', 'm1.4a_a : mcon periph must be enclosed by met1 for specific cells') |
| |
| m1.with_area(0..0.083).output("m1.6", "m1.6 : min. m1 area : 0.083um²") |
| |
| m1.holes.with_area(0..0.14).output("m1.7", "m1.7 : min. m1 with holes area : 0.14um²") |
| |
| if backend_flow = AL |
| #Could flag false positive, fix would be to add .rectangles for m1 |
| mconnotace_edges_with_less_enclosure_m1 = m1.enclosing(mconnotace, 0.06, projection).second_edges |
| error_corners_m1 = mconnotace_edges_with_less_enclosure_m1.width(angle_limit(100.0), 1.dbu) |
| mconnotace_interact_m1 = mconnotace.interacting(error_corners_m1.polygons(1.dbu)) |
| mconnotace_interact_m1.output("m1.5", "m1.5 : min. m1 enclosure of mcon of 2 adjacent edges : 0.06um") |
| end |
| log("END: 68/20 (m1)") |
| |
| # via |
| log("START: 68/44 (via)") |
| if backend_flow = AL |
| ringVIA = via.drc(with_holes > 0) |
| rectVIA = via.not(ringVIA) |
| via_not_mt = rectVIA.not(areaid_mt) |
| |
| via_not_mt.non_rectangles.output("via.1a", "via.1a : via outside of moduleCut should be rectangular") |
| via_not_mt.width(0.15, euclidian).output("via.1a_a", "via.1a_a : min. width of via outside of moduleCut : 0.15um") |
| # via_not_mt.edges.without_length(nil, 0.15 + 1.dbu).output("via.1a_b", "via.1a_b : maximum length of via : 0.15um") |
| via_not_mt.drc(length > 0.15).output("via.1a_b", "via.1a_b : maximum length of via : 0.15um") |
| |
| via.space(0.17, euclidian).output("via.2", "via.2 : min. via spacing : 0.17um") |
| |
| ringVIA.width(0.2, euclidian).output("via.3", "via.3 : min. width of ring-shaped via : 0.2um") |
| ringVIA.drc(width >= 0.205).output("via.3_a", "via.3_a : max. width of ring-shaped via : 0.205um") |
| |
| ringVIA.not(areaid_sl).output("via.3_b", "via.3_b: ring-shaped via must be enclosed by areaid_sl") |
| |
| m1.edges.enclosing(rectVIA.drc(width == 0.15), 0.055, euclidian).output("via.4a", "via.4a : min. m1 enclosure of 0.15um via : 0.055um") |
| rectVIA.squares.drc(width == 0.15).not(m1).output("via.4a_a", "via.4a_a : 0.15um via must be enclosed by met1") |
| |
| via1_edges_with_less_enclosure_m1 = m1.edges.enclosing(rectVIA.drc(width == 0.15), 0.085, projection).second_edges |
| error_corners_via1 = via1_edges_with_less_enclosure_m1.width(angle_limit(100.0), 1.dbu) |
| via2_interact = via.interacting(error_corners_via1.polygons(1.dbu)) |
| via2_interact.output("via.5a", "via.5a : min. m1 enclosure of 0.15um via of 2 adjacent edges : 0.085um") |
| |
| end |
| log("END: 68/44 (via)") |
| |
| # m2 |
| log("START: 69/20 (m2)") |
| m2.width(0.14, euclidian).output("m2.1", "m2.1 : min. m2 width : 0.14um") |
| |
| huge_m2 = m2.sized(-1.5).sized(1.5).snap(0.005) & m2 |
| non_huge_m2 = m2.edges - huge_m2 |
| huge_m2 = huge_m2.edges.outside_part(m2.merged) |
| via_outside_periphery = via.not(areaid_ce) |
| |
| non_huge_m2.space(0.14, euclidian).output("m2.2", "m2.2 : min. m2 spacing : 0.14um") |
| |
| (huge_m2.separation(non_huge_m2, 0.28, euclidian) + huge_m2.space(0.28, euclidian)).output("m2.3ab", "m2.3ab : min. 3um.m2 spacing m2 : 0.28um") |
| |
| m2.with_area(0..0.0676).output("m2.6", "m2.6 : min. m2 area : 0.0676um²") |
| m2.holes.with_area(0..0.14).output("m2.7", "m2.7 : min. m2 holes area : 0.14um²") |
| |
| if backend_flow = AL |
| m2.enclosing(via_outside_periphery, 0.055, euclidian).output("m2.4", "m2.4 : min. m2 enclosure of via : 0.055um") |
| via_outside_periphery.not(m2).output("m2.4_a", "m2.4_a : via in periphery must be enclosed by met2") |
| via_edges_with_less_enclosure_m2 = m2.enclosing(via, 0.085, projection).second_edges |
| error_corners = via_edges_with_less_enclosure_m2.width(angle_limit(100.0), 1.dbu) |
| via_interact = via.interacting(error_corners.polygons(1.dbu)) |
| via_interact.output("m2.5", "m2.5 : min. m2 enclosure of via of 2 adjacent edges : 0.085um") |
| |
| end |
| log("END: 69/20 (m2)") |
| |
| # via2 |
| log("START: 69/44 (via2)") |
| if backend_flow = AL |
| ringVIA2 = via2.drc(with_holes > 0) |
| rectVIA2 = via2.not(ringVIA2) |
| via2_not_mt = rectVIA2.not(areaid_mt) |
| via2_not_mt.non_rectangles.output("via2.1a", "via2.1a : via2 outside of moduleCut should be rectangular") |
| via2_not_mt.width(0.2, euclidian).output("via2.1a_a", "via2.1a_a : min. width of via2 outside of moduleCut : 0.2um") |
| via2_not_mt.edges.without_length(nil, 0.2 + 1.dbu).output("via2.1a_b", "via2.1a_b : maximum length of via2 : 0.2um") |
| via2.space(0.2, euclidian).output("via2.2", "via2.2 : min. via2 spacing : 0.2um") |
| ringVIA2.width(0.2, euclidian).output("via2.3", "via2.3 : min. width of ring-shaped via2 : 0.2um") |
| ringVIA2.drc(width >= 0.205).output("via2.3_a", "via2.3_a : max. width of ring-shaped via2 : 0.205um") |
| ringVIA2.not(areaid_sl).output("via2.3_b", "via2.3_b: ring-shaped via2 must be enclosed by areaid_sl") |
| m2.enclosing(via2, 0.04, euclidian).output("via2.4", "via2.4 : min. m2 enclosure of via2 : 0.04um") |
| via2.not(m2).output("via2.4_a", "via2.4_a : via must be enclosed by met2") |
| |
| via2_edges_with_less_enclosure = m2.enclosing(via2, 0.085, projection).second_edges |
| error_corners = via2_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu) |
| via2_interact = via2.interacting(error_corners.polygons(1.dbu)) |
| via2_interact.output("via2.5", "via2.5 : min. m3 enclosure of via2 of 2 adjacent edges : 0.085um") |
| end |
| log("END: 69/44 (via2)") |
| |
| # m3 |
| log("START: 70/20 (m3)") |
| m3.width(0.3, euclidian).output("m3.1", "m3.1 : min. m3 width : 0.3um") |
| |
| huge_m3 = m3.sized(-1.5).sized(1.5).snap(0.005) & m3 |
| non_huge_m3 = m3.edges - huge_m3 |
| huge_m3 = huge_m3.edges.outside_part(m3.merged) |
| |
| non_huge_m3.space(0.3, euclidian).output("m3.2", "m3.2 : min. m3 spacing : 0.3um") |
| |
| (huge_m3.separation(non_huge_m3, 0.4, euclidian) + huge_m3.space(0.4, euclidian)).output("m3.3cd", "m3.3cd : min. 3um.m3 spacing m3 : 0.4um") |
| |
| if backend_flow = AL |
| m3.enclosing(via2, 0.065, euclidian).output("m3.4", "m3.4 : min. m3 enclosure of via2 : 0.065um") |
| via2.not(m3).output("m3.4_a", "m3.4_a : via2 must be enclosed by met3") |
| end |
| log("END: 70/20 (m3)") |
| |
| # via3 |
| log("START: 70/44 (via3)") |
| if backend_flow = AL |
| |
| ringVIA3 = via3.drc(with_holes > 0) |
| rectVIA3 = via3.not(ringVIA3) |
| via3_not_mt = rectVIA3.not(areaid_mt) |
| via3_not_mt.non_rectangles.output("via3.1", "via3.1 : via3 outside of moduleCut should be rectangular") |
| via3_not_mt.width(0.2, euclidian).output("via3.1_a", "via3.1_a : min. width of via3 outside of moduleCut : 0.2um") |
| via3_not_mt.edges.without_length(nil, 0.2 + 1.dbu).output("via3.1_b", "via3.1_b : maximum length of via3 : 0.2um") |
| |
| via3.space(0.2, euclidian).output("via3.2", "via3.2 : min. via3 spacing : 0.2um") |
| m3.enclosing(via3, 0.06, euclidian).output("via3.4", "via3.4 : min. m3 enclosure of via3 : 0.06um") |
| rectVIA3.not(m3).output("via3.4_a", "via3.4_a : non-ring via3 must be enclosed by met3") |
| |
| via_edges_with_less_enclosure = m3.enclosing(via3, 0.09, projection).second_edges |
| error_corners = via_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu) |
| via3_interact = via3.interacting(error_corners.polygons(1.dbu)) |
| via3_interact.output("via3.5", "via3.5 : min. m3 enclosure of via3 of 2 adjacent edges : 0.09um") |
| end |
| log("END: 70/44 (via3)") |
| |
| # m4 |
| log("START: 71/20 (m4)") |
| m4.width(0.3, euclidian).output("m4.1", "m4.1 : min. m4 width : 0.3um") |
| |
| huge_m4 = m4.sized(-1.5).sized(1.5).snap(0.005) & m4 |
| non_huge_m4 = m4.edges - huge_m4 |
| huge_m4 = huge_m4.edges.outside_part(m4.merged) |
| |
| non_huge_m4.space(0.3, euclidian).output("m4.2", "m4.2 : min. m4 spacing : 0.3um") |
| |
| m4.with_area(0..0.240).output("m4.4a", "m4.4a : min. m4 area : 0.240um²") |
| |
| (huge_m4.separation(non_huge_m4, 0.4, euclidian) + huge_m4.space(0.4, euclidian)).output("m4.5ab", "m4.5ab : min. 3um.m4 spacing m4 : 0.4um") |
| |
| if backend_flow = AL |
| m4.enclosing(via3, 0.065, euclidian).output("m4.3", "m4.3 : min. m4 enclosure of via3 : 0.065um") |
| via3.not(m4).output("m4.3_a", "m4.3_a : via3 must be enclosed by met4") |
| end |
| log("END: 71/20 (m4)") |
| |
| # via4 |
| log("START: 71/44 (via4)") |
| ringVIA4 = via4.drc(with_holes > 0) |
| rectVIA4 = via4.not(ringVIA4) |
| via4_not_mt = rectVIA4.not(areaid_mt) |
| via4_not_mt.non_rectangles.output("via4.1", "via4.1 : via4 outside of moduleCut should be rectangular") |
| rectVIA4.width(0.8, euclidian).output("via4.1_a", "via4.1_a : min. width of via4 outside of moduleCut : 0.8um") |
| rectVIA4.drc(length > 0.8).output("via4.1_b", "via4.1_b : maximum length of via4 : 0.8um") |
| |
| via4.space(0.8, euclidian).polygons.output("via4.2", "via4.2 : min. via4 spacing : 0.8um") |
| ringVIA4.width(0.8, euclidian).output("via4.3", "via4.3 : min. width of ring-shaped via4 : 0.8um") |
| ringVIA4.drc(width >= 0.805).output("via4.3_a", "via4.3_a : max. width of ring-shaped via4 : 0.805um") |
| ringVIA4.not(areaid_sl).output("via4.3_b", "via4.3_b: ring-shaped via4 must be enclosed by areaid_sl") |
| m4.enclosing(via4, 0.19, euclidian).output("via4.4", "via4.4 : min. m4 enclosure of via4 : 0.19um") |
| rectVIA4.not(m4).output("via4.4_a", "via4.4_a : m4 must enclose all via4") |
| log("END: 71/44 (via4)") |
| |
| # m5 |
| log("START: 72/20 (m5)") |
| m5.width(1.6, euclidian).output("m5.1", "m5.1 : min. m5 width : 1.6um") |
| |
| m5.space(1.6, euclidian).output("m5.2", "m5.2 : min. m5 spacing : 1.6um") |
| |
| m5.enclosing(via4, 0.31, euclidian).output("m5.3", "m5.3 : min. m5 enclosure of via4 : 0.31um") |
| via4.not(m5).output("m5.3_a", "m5.3_a : via must be enclosed by m5") |
| |
| m5.with_area(0..4.0).output("m5.4", "m5.4 : min. m5 area : 4.0um²") |
| log("END: 72/20 (m5)") |
| |
| # pad |
| log("START: 76/20 (pad)") |
| pad.space(1.27, euclidian).output("pad.2", "pad.2 : min. pad spacing : 1.27um") |
| log("END: 76/20 (pad)") |
| |
| end #BEOL |
| |
| if FEOL |
| log("FEOL section") |
| |
| # hvi |
| log("START: 75/20 (hvi)") |
| hvi_peri = hvi.not(areaid_ce) |
| hvi_peri.width(0.6, euclidian).output("hvi.1", "hvi.1 : min. hvi width : 0.6um") |
| hvi_peri.space(0.7, euclidian).output("hvi.2a", "hvi.2a : min. hvi spacing : 0.7um") |
| log("END: 75/20 (hvi)") |
| |
| # hvntm |
| log("START: 125/20 (hvntm)") |
| hvntm_peri = hvntm.not(areaid_ce) |
| hvntm_peri.width(0.7, euclidian).output("hvntm.1", "hvntm.1 : min. hvntm width : 0.7um") |
| hvntm_peri.space(0.7, euclidian).output("hvntm.2", "hvntm.2 : min. hvntm spacing : 0.7um") |
| log("END: 125/20 (hvntm)") |
| |
| end #FEOL |
| |
| |
| if OFFGRID |
| log("OFFGRID-ANGLES section") |
| |
| dnwell.ongrid(0.005).output("dnwell_OFFGRID", "x.1b : OFFGRID vertex on dnwell") |
| dnwell.with_angle(0 .. 45).output("dnwell_angle", "x.3a : non 45 degree angle dnwell") |
| nwell.ongrid(0.005).output("nwell_OFFGRID", "x.1b : OFFGRID vertex on nwell") |
| nwell.with_angle(0 .. 45).output("nwell_angle", "x.3a : non 45 degree angle nwell") |
| pwbm.ongrid(0.005).output("pwbm_OFFGRID", "x.1b : OFFGRID vertex on pwbm") |
| pwbm.with_angle(0 .. 45).output("pwbm_angle", "x.3a : non 45 degree angle pwbm") |
| pwde.ongrid(0.005).output("pwde_OFFGRID", "x.1b : OFFGRID vertex on pwde") |
| pwde.with_angle(0 .. 45).output("pwde_angle", "x.3a : non 45 degree angle pwde") |
| hvtp.ongrid(0.005).output("hvtp_OFFGRID", "x.1b : OFFGRID vertex on hvtp") |
| hvtp.with_angle(0 .. 45).output("hvtp_angle", "x.3a : non 45 degree angle hvtp") |
| hvtr.ongrid(0.005).output("hvtr_OFFGRID", "x.1b : OFFGRID vertex on hvtr") |
| hvtr.with_angle(0 .. 45).output("hvtr_angle", "x.3a : non 45 degree angle hvtr") |
| lvtn.ongrid(0.005).output("lvtn_OFFGRID", "x.1b : OFFGRID vertex on lvtn") |
| lvtn.with_angle(0 .. 45).output("lvtn_angle", "x.3a : non 45 degree angle lvtn") |
| ncm.ongrid(0.005).output("ncm_OFFGRID", "x.1b : OFFGRID vertex on ncm") |
| ncm.with_angle(0 .. 45).output("ncm_angle", "x.3a : non 45 degree angle ncm") |
| diff.ongrid(0.005).output("diff_OFFGRID", "x.1b : OFFGRID vertex on diff") |
| tap.ongrid(0.005).output("tap_OFFGRID", "x.1b : OFFGRID vertex on tap") |
| diff.not(areaid_en.and(uhvi)).with_angle(0 .. 90).output("diff_angle", "x.2 : non 90 degree angle diff") |
| diff.and(areaid_en.and(uhvi)).with_angle(0 .. 45).output("diff_angle", "x.2c : non 45 degree angle diff") |
| tap.not(areaid_en.and(uhvi)).with_angle(0 .. 90).output("tap_angle", "x.2 : non 90 degree angle tap") |
| tap.and(areaid_en.and(uhvi)).with_angle(0 .. 45).output("tap_angle", "x.2c : non 45 degree angle tap") |
| tunm.ongrid(0.005).output("tunm_OFFGRID", "x.1b : OFFGRID vertex on tunm") |
| tunm.with_angle(0 .. 45).output("tunm_angle", "x.3a : non 45 degree angle tunm") |
| poly.ongrid(0.005).output("poly_OFFGRID", "x.1b : OFFGRID vertex on poly") |
| poly.with_angle(0 .. 90).output("poly_angle", "x.2 : non 90 degree angle poly") |
| rpm.ongrid(0.005).output("rpm_OFFGRID", "x.1b : OFFGRID vertex on rpm") |
| rpm.with_angle(0 .. 45).output("rpm_angle", "x.3a : non 45 degree angle rpm") |
| npc.ongrid(0.005).output("npc_OFFGRID", "x.1b : OFFGRID vertex on npc") |
| npc.with_angle(0 .. 45).output("npc_angle", "x.3a : non 45 degree angle npc") |
| nsdm.ongrid(0.005).output("nsdm_OFFGRID", "x.1b : OFFGRID vertex on nsdm") |
| nsdm.with_angle(0 .. 45).output("nsdm_angle", "x.3a : non 45 degree angle nsdm") |
| psdm.ongrid(0.005).output("psdm_OFFGRID", "x.1b : OFFGRID vertex on psdm") |
| psdm.with_angle(0 .. 45).output("psdm_angle", "x.3a : non 45 degree angle psdm") |
| licon.ongrid(0.005).output("licon_OFFGRID", "x.1b : OFFGRID vertex on licon") |
| licon.with_angle(0 .. 90).output("licon_angle", "x.2 : non 90 degree angle licon") |
| li.ongrid(0.005).output("li_OFFGRID", "x.1b : OFFGRID vertex on li") |
| li.with_angle(0 .. 45).output("li_angle", "x.3a : non 45 degree angle li") |
| mcon.ongrid(0.005).output("ct_OFFGRID", "x.1b : OFFGRID vertex on mcon") |
| mcon.with_angle(0 .. 90).output("ct_angle", "x.2 : non 90 degree angle mcon") |
| vpp.ongrid(0.005).output("vpp_OFFGRID", "x.1b : OFFGRID vertex on vpp") |
| vpp.with_angle(0 .. 45).output("vpp_angle", "x.3a : non 45 degree angle vpp") |
| m1.ongrid(0.005).output("m1_OFFGRID", "x.1b : OFFGRID vertex on m1") |
| m1.with_angle(0 .. 45).output("m1_angle", "x.3a : non 45 degree angle m1") |
| via.ongrid(0.005).output("via_OFFGRID", "x.1b : OFFGRID vertex on via") |
| via.with_angle(0 .. 90).output("via_angle", "x.2 : non 90 degree angle via") |
| m2.ongrid(0.005).output("m2_OFFGRID", "x.1b : OFFGRID vertex on m2") |
| m2.with_angle(0 .. 45).output("m2_angle", "x.3a : non 45 degree angle m2") |
| via2.ongrid(0.005).output("via2_OFFGRID", "x.1b : OFFGRID vertex on via2") |
| via2.with_angle(0 .. 90).output("via2_angle", "x.2 : non 90 degree angle via2") |
| m3.ongrid(0.005).output("m3_OFFGRID", "x.1b : OFFGRID vertex on m3") |
| m3.with_angle(0 .. 45).output("m3_angle", "x.3a : non 45 degree angle m3") |
| via3.ongrid(0.005).output("via3_OFFGRID", "x.1b : OFFGRID vertex on via3") |
| via3.with_angle(0 .. 90).output("via3_angle", "x.2 : non 90 degree angle via3") |
| nsm.ongrid(0.005).output("nsm_OFFGRID", "x.1b : OFFGRID vertex on nsm") |
| nsm.with_angle(0 .. 45).output("nsm_angle", "x.3a : non 45 degree angle nsm") |
| m4.ongrid(0.005).output("m4_OFFGRID", "x.1b : OFFGRID vertex on m4") |
| m4.with_angle(0 .. 45).output("m4_angle", "x.3a : non 45 degree angle m4") |
| via4.ongrid(0.005).output("via4_OFFGRID", "x.1b : OFFGRID vertex on via4") |
| via4.with_angle(0 .. 90).output("via4_angle", "x.2 : non 90 degree angle via4") |
| m5.ongrid(0.005).output("m5_OFFGRID", "x.1b : OFFGRID vertex on m5") |
| m5.with_angle(0 .. 45).output("m5_angle", "x.3a : non 45 degree angle m5") |
| pad.ongrid(0.005).output("pad_OFFGRID", "x.1b : OFFGRID vertex on pad") |
| pad.with_angle(0 .. 45).output("pad_angle", "x.3a : non 45 degree angle pad") |
| mf.ongrid(0.005).output("mf_OFFGRID", "x.1b : OFFGRID vertex on mf") |
| mf.with_angle(0 .. 90).output("mf_angle", "x.2 : non 90 degree angle mf") |
| hvi.ongrid(0.005).output("hvi_OFFGRID", "x.1b : OFFGRID vertex on hvi") |
| hvi.with_angle(0 .. 45).output("hvi_angle", "x.3a : non 45 degree angle hvi") |
| hvntm.ongrid(0.005).output("hvntm_OFFGRID", "x.1b : OFFGRID vertex on hvntm") |
| hvntm.with_angle(0 .. 45).output("hvntm_angle", "x.3a : non 45 degree angle hvntm") |
| vhvi.ongrid(0.005).output("vhvi_OFFGRID", "x.1b : OFFGRID vertex on vhvi") |
| vhvi.with_angle(0 .. 45).output("vhvi_angle", "x.3a : non 45 degree angle vhvi") |
| uhvi.ongrid(0.005).output("uhvi_OFFGRID", "x.1b : OFFGRID vertex on uhvi") |
| uhvi.with_angle(0 .. 45).output("uhvi_angle", "x.3a : non 45 degree angle uhvi") |
| pwell_rs.ongrid(0.005).output("pwell_rs_OFFGRID", "x.1b : OFFGRID vertex on pwell_rs") |
| pwell_rs.with_angle(0 .. 45).output("pwell_rs_angle", "x.3a : non 45 degree angle pwell_rs") |
| areaid_re.ongrid(0.005).output("areaid_re_OFFGRID", "x.1b : OFFGRID vertex on areaid.re") |
| |
| end #OFFGRID |