diff --git a/scripts/tech-files/sky130A_mr.drc b/scripts/tech-files/sky130A_mr.drc deleted file mode 100644 index fdd2962d..00000000 --- a/scripts/tech-files/sky130A_mr.drc +++ /dev/null @@ -1,794 +0,0 @@ -# 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 : -# 2022-6-22 : 2022.6.30_01.07 release -# -########################################################################################## -release = "2022.6.30_01.07" - -require 'time' -require "logger" - -exec_start_time = Time.now - -logger = Logger.new(STDOUT) - -logger.formatter = proc do |severity, datetime, progname, msg| - "#{msg} -" -end -# 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 - -FEOL = false -BEOL = false -OFFGRID = false -SEAL = false -FLOATING_MET = false - -# enable / disable rule groups -if $feol == "1" || $feol == "true" - FEOL = true # front-end-of-line checks -else - FEOL = false -end - -if $beol == "1" || $beol == "true" - BEOL = true # back-end-of-line checks -else - BEOL = false -end - -if $offgrid == "1" || $offgrid == "true" - OFFGRID = true # manufacturing grid/angle checks -else - OFFGRID = false -end - -if $seal == "1" || $seal == "true" - SEAL = true # SEAL RING checks -else - SEAL = false -end - -if $floating_met == "1" || $floating_met == "true" - FLOATING_MET = true # back-end-of-line checks -else - FLOATING_MET = false -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" - -nsdm_wildcard = "93/44" - -psdm_wildcard = "94/20" -nwell_wildcard = "64/20" - -diff = input(65, 20) -tap = polygons(65, 44) -nwell = polygons(nwell_wildcard) -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(nsdm_wildcard) -psdm = polygons(psdm_wildcard) -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)") - -not_sram = layout(source.cell_obj).select("-*sky130_sram_*kbyte_*") -not_sram_nsdm = not_sram.input(nsdm_wildcard) -not_sram_psdm = not_sram.input(psdm_wildcard) -not_sram_nwell = not_sram.input(nwell_wildcard) - -# This is a hack, should be reverted - -not_io = layout(source.cell_obj).select("-*sky130_fd_io__gpiov2_amux", "-*sky130_fd_io__simple_pad_and_busses") -not_io_nwell = not_io.input(nwell_wildcard) - -# 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") -nwell_interact = not_sram_nwell.and(not_io_nwell).merge -dnwell.enclosing(nwell_interact.holes, 1.03, euclidian).output("nwell.6", "nwell.6 : min enclosure of nwellHole by dnwell : 1.03um") -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.outside(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 manually merged if less than : 0.27um") -log("END: 95/20 (npc)") - -# nsdm -log("START: 93/44 (nsdm)") -not_sram_nsdm.outside(areaid_ce).width(0.38, euclidian).output("nsd.1", "nsd.1 : min. nsdm width : 0.38um") -not_sram_nsdm.not(areaid_ce).space(0.38, euclidian).output("nsd.2", "nsd.2 : min. nsdm spacing, should be manually merged if less than : 0.38um") -log("END: 93/44 (nsdm)") - -# psdm -log("START: 94/20 (psdm)") -not_sram_psdm.outside(areaid_ce).width(0.38, euclidian).output("psd.1", "psd.1 : min. psdm width : 0.38um") -not_sram_psdm.not(areaid_ce).space(0.38, euclidian).output("psd.2", "psd.2 : min. psdm spacing, should be manually merged if less than : 0.38um") -log("END: 94/20 (psdm)") - -# licon -log("START: 66/44 (licon)") -if SEAL - ringLICON = licon.drc(with_holes > 0) - rectLICON = licon.not(ringLICON) -else - rectLICON = licon -end -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)") -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.interacting(capm)).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") -(m3.not_interacting(capm)).separation(capm, 0.5, euclidian).output("capm.11", "capm.11 : Min spacing of capm and met3 not overlapping capm : 0.5um") -log("END: 89/44 (capm)") - -# CAP2M -log("START: 97/44 (cap2m)") -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") -(m4.interacting(cap2m)).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") -(m4.not_interacting(cap2m)).separation(cap2m, 0.5, euclidian).output("cap2m.11", "cap2m.11 : Min spacing of cap2m and met4 not overlapping cap2m : 0.5um") -log("END: 97/44 (cap2m)") -end #FEOL - -if BEOL -log("BEOL section") - -# li -log("START: 67/20 (li)") -linotace = li.not(li.interacting(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.edges.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") -linotace.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) -if SEAL - ringMCON = mcon.drc(with_holes > 0) - rectMCON = mcon.not(ringMCON) -else - rectMCON = mcon -end -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") -if SEAL - 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") -end -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 FLOATING_MET - m1.not_interacting(via.or(mcon)).output("m1.x", "floating met1, must interact with via1") -end - -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 - if SEAL - ringVIA = via.drc(with_holes > 0) - rectVIA = via.not(ringVIA) - else - rectVIA = via - end - - 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") - - if SEAL - 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") - end - - 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 FLOATING_MET - m2.not_interacting(via.or(via2)).output("m2.x", "floating met2, must interact with via1 or via2") -end -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 - if SEAL - ringVIA2 = via2.drc(with_holes > 0) - rectVIA2 = via2.not(ringVIA2) - else - rectVIA2 = via2 - end - - 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") - - if SEAL - 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") - end - - 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 FLOATING_MET - m3.not_interacting(via2.or(via3)).output("m3.x", "floating met3, must interact with via2 or via3") -end -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 - if SEAL - ringVIA3 = via3.drc(with_holes > 0) - rectVIA3 = via3.not(ringVIA3) - else - rectVIA3 = via3 - end - - 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 FLOATING_MET - m4.not_interacting(via3.or(via4)).output("m4.x", "floating met3, must interact with via3 or via4") -end -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)") -if SEAL - ringVIA4 = via4.drc(with_holes > 0) - rectVIA4 = via4.not(ringVIA4) -else - rectVIA4 = via4 -end - -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") - -if SEAL - 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") -end - -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") -if FLOATING_MET - m5.not_interacting(via4).output("m5.x", "floating met5, must interact with via4") -end -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 -logger.info(" ") -logger.info("Cell exclusion list:") -logger.info(" rule | cell") -logger.info(" nwell.6 | sky130_fd_io__gpiov2_amux, sky130_fd_io__simple_pad_and_busses, sram") -logger.info(" nsd.1 | sram") -logger.info(" nsd.2 | sram") -logger.info(" psd.1 | sram") -logger.info(" psd.2 | sram") -logger.info(" ") -logger.info("release #{release}") \ No newline at end of file