# 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 # # 2023-6-14 : 2023.6.14_01.08 release # # 2024-2-11 : 2024.2.11_01.09 release # # 2024-5-26 : Update rectMCON_peri to use outside instead of not # 2024-8-15 : # * Avoid assigning to constant variables # * Fix defaults for arguments when they are not passed in the CLI # # 2024-8-18 : # * Add new rules: # * `MR_lvtn.OVL.2` # * `MR_tunm.CON.1` # * `rdl.1` # * `rdl.2` # * `MR_thkox.CON.1` # * `MR_rdl.CON.1` # * `MR_li.WID.4` # * `moduleCut.1` # * Modify `licon` checks to account for ("non-bar" | exempt prec_resistor) # * Rename `licon` checks # 2024-9-1 : # * Change the default of sram_exclude to false ########################################################################################## release = "2024.2.11_01.09" 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 source($input, $top_cell) if $input if $report report("SKY130 DRC runset", $report) else report( "SKY130 DRC runset", File.join(File.dirname(RBA::CellView.active.filename), "sky130_drc.txt") ) end if not defined? $feol $feol = "false" end if not defined? $beol $beol = "false" end if not defined? $offgrid $offgrid = "false" end if not defined? $floating_met $floating_met = "false" end if not defined? $sram_exclude $sram_exclude = "false" 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" FEOL = false else FEOL = true # front-end-of-line checks end if $beol == "0" || $beol == "false" BEOL = false else BEOL = true # back-end-of-line checks end if $offgrid == "0" || $offgrid == "false" OFFGRID = false else OFFGRID = true # manufacturing grid/angle checks end if $seal == "0" || $seal == "false" SEAL = false else SEAL = true # SEAL RING checks end if $floating_met == "0" || $floating_met == "false" FLOATING_MET = false else FLOATING_MET = true # back-end-of-line checks end if $sram_exclude == "0" || $sram_exclude == "false" SRAM_EXCLUDE = false # back-end-of-line checks else SRAM_EXCLUDE = true end logger.info("Args:") logger.info("\tsram_exclude: #{$sram_exclude}") logger.info("\tfeol: #{$feol}") logger.info("\tbeol: #{$beol}") logger.info("\tfloating_met: #{$floating_met}") logger.info("\toffgrid: #{$offgrid}") logger.info("\tseal: #{$seal}") # 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 $thr ? threads($thr) : threads(4) # 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" li_res_wildcard = "67/13" 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) li_res = polygons(li_res_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) rdl = polygons(74, 20) 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) poly_fill = polygons(66, 99) diff_rs = polygons(65, 13) pwell_rs = polygons(64, 13) li_rs = polygons(67, 13) cfom = polygons(22, 20) thkox = polygons(75, 21) areaid_ce_merged = areaid_ce.merged # 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| new_data.insert(p) if p.holes >= (min || 0) && (!max || p.holes <= max) 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.1", "dnwell.1: min. dnwell width : 3.0um" ) dnwell .not(uhvi.or(vhvi)) .space(6.3, euclidian) .output("dnwell.2", "dnwell.2 : min. dnwell spacing : 6.3um") 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" ) hvmarker = hvi.or(rdl).or(uhvi).or(vhvi) nwell .interacting(nwell.and(hvmarker)) .not(hvmarker) .output("nwell.9", "nwell.9 : HVnwell must be enclosed by hv marker") 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)") # pwde log("START: 124/20 (pwde)") pwde.width(0.84, euclidian).output( "pwde.1", "pwde.1 : min. pwde width : 0.84um" ) pwde .and(uhvi.or(vhvi)) .space(1.27, euclidian) .output("pwde.2", "pwde.2 : min. pwde inside v20 spacing : 1.27um") log("END: 124/20 (pwde)") # 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" ) lvtn .interacting(nwell) .not_inside(nwell) .edges .and(nwell) .output("MR_lvtn.OVL.2", "MR_lvtn.OVL.2 : lvtn must not straddle nwell") 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" ) diff .inside(areaid_ce) .width(0.14, euclidian) .output( "difftap.2", "difftap.2 : min. diff width inside areadid:ce : 0.14um" ) 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" ) tunm.output( "MR_tunm.CON.1", "MR_tunm.CON.1 : use of layer tunm is prohibited" ) log("END: 80/20 (tunm)") # thkox log("START: 80/20 (thkox)") thkox_peri = thkox.not(areaid_ce) thkox_peri.width(0.6, euclidian).output( "thkox.1", "thkox.1 : min. thkox width inside periphery : 0.6um" ) thkox_peri.space(0.7, euclidian).output( "thkox.2", "thkox.2 : min. thkox spacing inside periphery : 0.7um" ) thkox .interacting(diff) .not_inside(diff) .edges .and(diff) .output("MR_thkox.CON.1", "MR_thkox.CON.1 : thkox must not straddle diff") log("END: 80/20 (thkox)") # 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") core_poly_gap = poly.inside(areaid_ce).drc(width(projection) <= 0.15).polygons poly .interacting(core_poly_gap) .isolated(0.16, projection) .output("poly.3", "poly.3 : min. poly spacing inside areaid:core : 0.16um") log("END: 66/20 (poly)") # ldntm log("START: 80/20 (ldntm)") ldntm_core = ldntm.and(areaid_ce) ldntm_core.width(0.7, euclidian).output( "ldntm.1", "ldntm.1 : min. ldntm width inside areaid:core : 0.7um" ) ldntm_core.space(0.7, euclidian).output( "ldntm.2", "ldntm.2 : min. ldntm spacing inside areaid:core : 0.7um" ) log("END: 80/20 (ldntm)") # 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)") # rdl log("START: 74/20 (rdl)") rdl.width(10.um, euclidian).output("rdl.1", "rdl.1 : min. rdl width : 10um") rdl.space(10.um, euclidian).output("rdl.2", "rdl.2 : min. rdl spacing : 10um") rdl.output("MR_rdl.CON.1", "MR_rdl.CON.1 : use of rdl layer is prohibited") log("END: 74/20 (rdl)") # 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)") if SRAM_EXCLUDE not_sram_nsdm .not(areaid_ce) .space(0.38, euclidian) .output("nsdm.1", "nsdm.1 : min. nsdm spacing in periphery : 0.38um") not_sram_nsdm .outside(areaid_ce) .width(0.38, euclidian) .output("nsdm.2", "nsdm.2 : min. nsdm width in periphery : 0.38um") not_sram_nsdm_width = not_sram_nsdm .width(0.38, euclidian) .polygons .interacting(areaid_ce_merged) not_sram_nsdm_width.interacting( not_sram_nsdm_width.edges.outside_part(areaid_ce_merged) ).output("nsdm.3", "nsdm.3 : min. diff width across areaid:ce : 0.38um") nsdm_space = not_sram_nsdm .space(0.38, euclidian) .polygons .interacting(areaid_ce_merged) nsdm_space.interacting( nsdm_space.edges.outside_part(areaid_ce_merged) ).output("nsdm.4", "nsdm.4 : min. spacing across areaid:ce : 0.38um") not_sram_nsdm .inside(areaid_ce) .width(0.29, euclidian) .output("nsdm.5", "nsdm.5 : min. nsdm width in areaid:ce : 0.29um") not_sram_nsdm .inside(areaid_ce) .space(0.29, euclidian) .output("nsdm.6", "nsdm.6 : min. nsdm spacing in areaid:ce : 0.29um") not_sram_nsdm .inside(areaid_ce) .space(0.38, projection) .with_internal_angle(0) .output( "nsdm.6a", "nsdm.6a : min. nsdm spacing on parallel edges in areaid:ce : 0.38um" ) else nsdm .not(areaid_ce) .space(0.38, euclidian) .output("nsdm.1", "nsdm.1 : min. nsdm spacing in periphery : 0.38um") nsdm .outside(areaid_ce) .width(0.38, euclidian) .output("nsdm.2", "nsdm.2 : min. nsdm width in periphery : 0.38um") nsdm_width = nsdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged) nsdm_width.interacting( nsdm_width.edges.outside_part(areaid_ce_merged) ).output("nsdm.3", "nsdm.3 : min. diff width across areaid:ce : 0.38um") nsdm_space = nsdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged) nsdm_space.interacting( nsdm_space.edges.outside_part(areaid_ce_merged) ).output("nsdm.4", "nsdm.4 : min. spacing across areaid:ce : 0.38um") nsdm .inside(areaid_ce) .width(0.29, euclidian) .output("nsdm.5", "nsdm.5 : min. nsdm width in areaid:ce : 0.29um") nsdm .inside(areaid_ce) .space(0.29, euclidian) .output("nsdm.6", "nsdm.6 : min. nsdm spacing in areaid:ce : 0.29um") nsdm .inside(areaid_ce) .space(0.38, projection) .with_internal_angle(0) .output( "nsdm.6a", "nsdm.6a : min. nsdm spacing on parallel edges in areaid:ce : 0.38um" ) end # SRAM_EXCLUDE log("END: 93/44 (nsdm)") # psdm log("START: 94/20 (psdm)") if SRAM_EXCLUDE not_sram_psdm .not(areaid_ce) .space(0.38, euclidian) .output("psdm.1", "psdm.1 : min. psdm spacing in periphery : 0.38um") not_sram_psdm .outside(areaid_ce) .width(0.38, euclidian) .output("psdm.2", "psdm.2 : min. psdm width in periphery : 0.38um") not_sram_psdm_width = not_sram_psdm .width(0.38, euclidian) .polygons .interacting(areaid_ce_merged) not_sram_psdm_width.interacting( not_sram_psdm_width.edges.outside_part(areaid_ce_merged) ).output("psdm.3", "psdm.3 : min. diff width across areaid:ce : 0.38um") psdm_space = not_sram_psdm .space(0.38, euclidian) .polygons .interacting(areaid_ce_merged) psdm_space.interacting( psdm_space.edges.outside_part(areaid_ce_merged) ).output("psdm.4", "psdm.4 : min. spacing across areaid:ce : 0.38um") not_sram_psdm .inside(areaid_ce) .width(0.29, euclidian) .output("psdm.5", "psdm.5 : min. psdm width in areaid:ce : 0.29um") not_sram_psdm .inside(areaid_ce) .space(0.29, euclidian) .output("psdm.6", "psdm.6 : min. psdm spacing in areaid:ce : 0.29um") not_sram_psdm .inside(areaid_ce) .space(0.38, projection) .with_internal_angle(0) .output( "psdm.6a", "psdm.6a : min. psdm spacing on parallel edges in areaid:ce : 0.38um" ) not_sram_psdm .and(not_sram_nsdm) .and(diff) .output( "nsdm_psdm_overlap", "nsdm_psdm_overlap : nsdm overlaps psdm over active" ) else psdm .not(areaid_ce) .space(0.38, euclidian) .output("psdm.1", "psdm.1 : min. psdm spacing in periphery : 0.38um") psdm .outside(areaid_ce) .width(0.38, euclidian) .output("psdm.2", "psdm.2 : min. psdm width in periphery : 0.38um") psdm_width = psdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged) psdm_width.interacting( psdm_width.edges.outside_part(areaid_ce_merged) ).output("psdm.3", "psdm.3 : min. diff width across areaid:ce : 0.38um") psdm_space = psdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged) psdm_space.interacting( psdm_space.edges.outside_part(areaid_ce_merged) ).output("psdm.4", "psdm.4 : min. spacing across areaid:ce : 0.38um") psdm .inside(areaid_ce) .width(0.29, euclidian) .output("psdm.5", "psdm.5 : min. psdm width in areaid:ce : 0.29um") psdm .inside(areaid_ce) .space(0.29, euclidian) .output("psdm.6", "psdm.6 : min. psdm spacing in areaid:ce : 0.29um") psdm .inside(areaid_ce) .space(0.38, projection) .with_internal_angle(0) .output( "psdm.6a", "psdm.6a : min. psdm spacing on parallel edges in areaid:ce : 0.38um" ) psdm .and(nsdm) .and(diff) .output( "nsdm_psdm_overlap", "nsdm_psdm_overlap : nsdm overlaps psdm over active" ) end # SRAM_EXCLUDE log("END: 94/20 (psdm)") # licon log("START: 66/44 (licon)") polyi = poly_fill.and(poly) poly_licon = (polyi.and(licon)).and(areaid_ce) licon_peri = licon.outside(areaid_ce) if SEAL ringLICON = licon.drc(with_holes > 0) rectLICON = licon.not(ringLICON) else rectLICON = licon end rectLICON.non_rectangles.output( "licon.1_c", "licon.1_c : licon should be rectangle" ) prec_resistor = (rpm | urpm) & psdm & (poly.interacting(poly_rs)) licon_peri.not(prec_resistor).space(0.17, euclidian).output( "MR_licon.SP.1", "MR_licon.SP.1: min. licon spacing in periphery : 0.17um" ) licon_not_prec_resistor = licon.not(prec_resistor) licon_not_prec_resistor.drc(length != 0.17).output( "licon.1", "licon.1: min/max. licon length : 0.17um" ) licon_peri .and(difftap) .separation(npc, 0.09, euclidian) .output( "licon.13", "licon.13: min. licon on diff spacing to npc in periphery : 0.09um" ) licon_peri .and(difftap) .and(npc) .output( "licon.13_a", "licon.13_a: licon on diff in periphery can't overlap npc" ) licon .and(poly) .and(difftap) .output( "licon.17", "licon.17 : Licons may not overlap both poly and (diff or tap)" ) xfom = difftap.not(poly) licon1ToXfom = licon.interacting(licon.and(xfom)) polyLicon1 = (licon.not(licon1ToXfom)).interacting((licon.not(licon1ToXfom)).and(poly)) polyLicon1_CORE = polyLicon1.and(areaid_ce) npc.enclosing(polyLicon1_CORE, 0.045, euclidian).output( "licon.6", "licon.6 : npc min enclosure of poly_licon inside areaid:core : 0.045um" ) polyLicon1_CORE.not(npc).output( "licon.6", "licon.6 : npc min enclosure of poly_licon inside areaid:core : 0.045um" ) log("END: 66/44 (licon)") #nsm log("START: 61/20 (nsm)") nsm.space(4.0, euclidian).output("nsm.1", "nsm.1 : nsm min. spacing : 4.0um") nsm.width(3.0, euclidian).output("nsm.2", "nsm.2 : nsm min. width : 3.0um") log("END: 61/20 (nsm)") # 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_bot_plate = ((capm.and(m3)).sized(0.14)) m3_bot_plate .isolated(1.2, euclidian) .polygons .not(m3) .output("capm.2b", "capm.2b : min. spacing between met3_bot_plate : 1.2um") (m3.not_interacting(m3_bot_plate)).separation( m3_bot_plate, 1.2, euclidian ).output( "capm.2b_a", "capm.2b_a : min. spacing between met3_bot_plate and met3 : 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", "capm.3 : 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.2um" ) cap2m.separation(via4, 0.2, euclidian).output( "cap2m.5", "cap2m.5 : min. cap2m spacing to via4 : 0.2um" ) (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)") li_outside_or_touching_areaidce = (li.outside(areaid_ce)).or( li.interacting(areaid_ce).not(li.inside(areaid_ce)) ) li_outside_or_touching_areaidce.width(0.17, euclidian).output( "li.1", "li.1 : min. li width outside or crossing areaid:ce : 0.17um" ) li_outside_or_touching_areaidce.space(0.17, euclidian).output( "li.3", "li.3 : min. li spacing outside or crossing areaid:ce : 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 = li.not(li.interacting(areaid_ce)) linotace.with_area(nil, 0.0561).output( "li.6", "li.6 : min. li area : 0.0561um²" ) li_core = li.and(areaid_ce) li_core.space(0.14, euclidian).output( "li.7", "li.7 : min. li core spacing : 0.14um" ) li_core.width(0.14, euclidian).output( "li.8", "li.8 : min. li core width : 0.14um" ) log("END: 67/20 (li)") # li res log("START: 67/13 (li_res)") li_res.width(0.29, euclidian).output( "MR_li.WID.4", "MR_li.WID.4 : li:res minimum width : 0.29um" ) log("END: 67/13 (li_res)") # 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.outside(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)") via_inside_periphery = via.and(areaid_ce) 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" ) m2.enclosing(via_inside_periphery, 0.045, euclidian).output( "m2.4_b", "m2.4_B : min. m2 enclosure of via inside areaid:core : 0.045um" ) via_edges_with_less_enclosure_m2 = m2.enclosing(via_outside_periphery, 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 m3.with_area(0..0.240).output("m3.6", "m3.6 : min. m3 area : 0.240um²") m3 .holes .with_area(0..0.2) .output("m3.7", "m3.7 : min. m3 holes area : 0.2um²") 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 m4 .holes .with_area(0..0.2) .output("m4.7", "m4.7 : min. m4 holes area : 0.2um²") 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²") m5 .holes .with_area(0..0.14) .output("m5.7", "m5.7 : min. m5 holes area : 0.14um²") 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)") log("START: 81/10 (moduleCut)") areaid_mt.output( "moduleCut.1", "moduleCut.1 : moduleCut layer is for SkyWater use only" ) log("END: 81/10 (moduleCut)") #areaid_mt = polygons(81, 10) 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") if SRAM_EXCLUDE 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") else logger.info( " nwell.6 | sky130_fd_io__gpiov2_amux, sky130_fd_io__simple_pad_and_busses" ) end #SRAM_EXCLUDE logger.info(" ") logger.info("release #{release}")