diff --git a/checks/drc_checks/klayout/klayout_gds_drc_check.py b/checks/drc_checks/klayout/klayout_gds_drc_check.py index aeeaa93..cadd159 100644 --- a/checks/drc_checks/klayout/klayout_gds_drc_check.py +++ b/checks/drc_checks/klayout/klayout_gds_drc_check.py @@ -60,12 +60,13 @@ def klayout_gds_drc_check(check_name, drc_script_path, gds_input_file_path, outp parser.add_argument('--feol', '-f', action='store_true', required=False, default=False, help='run FEOL rules') parser.add_argument('--beol', '-b', action='store_true', required=False, default=False, help='run BEOL rules') parser.add_argument('--off_grid', '-og', action='store_true', required=False, default=False, help='run OFFGRID rules') + parser.add_argument('--pdk', required=False, default="sky130A", help='PDK') args = parser.parse_args() gds_input_file_path = Path(args.gds_input_file_path) output_directory = Path(args.output_directory) - klayout_sky130A_mr_drc_script_path = Path(__file__).parent.parent.parent / "tech-files/gf180mcuD_mr.drc" + klayout_sky130A_mr_drc_script_path = Path(__file__).parent.parent.parent / f"tech-files/{args.pdk}_mr.drc" extra_args = [] if args.feol: diff --git a/checks/tech-files/sky130A_mr.drc b/checks/tech-files/sky130A_mr.drc index 2de50cc..e3666d5 100644 --- a/checks/tech-files/sky130A_mr.drc +++ b/checks/tech-files/sky130A_mr.drc @@ -11,79 +11,116 @@ # # 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 "time" require "logger" exec_start_time = Time.now logger = Logger.new(STDOUT) -logger.formatter = proc do |severity, datetime, progname, msg| - "#{msg} +logger.formatter = + proc do |severity, datetime, progname, msg| + "#{msg} " -end + 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 +source($input, $top_cell) if $input if $report - report("SKY130 DRC runset", $report) + report("SKY130 DRC runset", $report) else - report("SKY130 DRC runset", File.join(File.dirname(RBA::CellView::active.filename), "sky130_drc.txt")) + 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 +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 -SRAM_EXCLUDE = true - # enable / disable rule groups -if $feol == "1" || $feol == "true" - FEOL = true # front-end-of-line checks +if $feol == "0" || $feol == "false" + FEOL = false else - FEOL = false + FEOL = true # front-end-of-line checks end -if $beol == "1" || $beol == "true" - BEOL = true # back-end-of-line checks +if $beol == "0" || $beol == "false" + BEOL = false else - BEOL = false + BEOL = true # back-end-of-line checks end -if $offgrid == "1" || $offgrid == "true" - OFFGRID = true # manufacturing grid/angle checks +if $offgrid == "0" || $offgrid == "false" + OFFGRID = false else - OFFGRID = false + OFFGRID = true # manufacturing grid/angle checks end -if $seal == "1" || $seal == "true" - SEAL = true # SEAL RING checks +if $seal == "0" || $seal == "false" + SEAL = false else - SEAL = false + SEAL = true # SEAL RING checks end -if $floating_met == "1" || $floating_met == "true" - FLOATING_MET = true # back-end-of-line checks +if $floating_met == "0" || $floating_met == "false" + FLOATING_MET = false else - FLOATING_MET = false + FLOATING_MET = true # back-end-of-line checks end if $sram_exclude == "0" || $sram_exclude == "false" - SRAM_EXCLUDE = false + SRAM_EXCLUDE = false # back-end-of-line checks else - SRAM_EXCLUDE = true + 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 ######################## @@ -96,11 +133,7 @@ end # hierachical deep -if $thr - threads($thr) -else - threads(4) -end +$thr ? threads($thr) : threads(4) # if more inof is needed, set true # verbose(true) @@ -111,6 +144,7 @@ verbose(true) # 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" @@ -155,6 +189,7 @@ 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) @@ -228,20 +263,19 @@ 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| - if p.holes >= (min || 0) && (!max || p.holes <= max) - new_data.insert(p) - end + 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 - DRC::DRCLayer::new(@engine, new_data) - end end # DRC section @@ -249,664 +283,1347 @@ end 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") -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") -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") -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)") - -# 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") - nsdm_width = not_sram_nsdm.rectangles.width(0.38, euclidian).polygons - nsdm_cross_areaid_ce = nsdm_width.edges.outside_part(areaid_ce).not(nsdm_width.outside(areaid_ce).edges) - nsdm_cross_areaid_ce.output("nsdm.3", "nsdm.3 : min. diff width across areaid:ce : 0.38um") - nsdm_space = not_sram_nsdm.rectangles.space(0.38, euclidian).polygons - nsdm_cross_areaid_ce = nsdm_space.edges.outside_part(areaid_ce).not(nsdm_space.outside(areaid_ce).edges) - nsdm_cross_areaid_ce.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, euclidian).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.rectangles.width(0.38, euclidian).polygons - nsdm_cross_areaid_ce = nsdm_width.edges.outside_part(areaid_ce).not(nsdm_width.outside(areaid_ce).edges) - nsdm_cross_areaid_ce.output("nsdm.3", "nsdm.3 : min. diff width across areaid:ce : 0.38um") - nsdm_space = nsdm.rectangles.space(0.38, euclidian).polygons - nsdm_cross_areaid_ce = nsdm_space.edges.outside_part(areaid_ce).not(nsdm_space.outside(areaid_ce).edges) - nsdm_cross_areaid_ce.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, euclidian).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") - psdm_width = not_sram_psdm.rectangles.width(0.38, euclidian).polygons - psdm_cross_areaid_ce = psdm_width.edges.outside_part(areaid_ce).not(psdm_width.outside(areaid_ce).edges) - psdm_cross_areaid_ce.output("psdm.3", "psdm.3 : min. diff width across areaid:ce : 0.38um") - psdm_space = not_sram_psdm.rectangles.space(0.38, euclidian).polygons - psdm_cross_areaid_ce = psdm_space.edges.outside_part(areaid_ce).not(psdm_space.outside(areaid_ce).edges) - psdm_cross_areaid_ce.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, euclidian).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.rectangles.width(0.38, euclidian).polygons - psdm_cross_areaid_ce = psdm_width.edges.outside_part(areaid_ce).not(psdm_width.outside(areaid_ce).edges) - psdm_cross_areaid_ce.output("psdm.3", "psdm.3 : min. diff width across areaid:ce : 0.38um") - psdm_space = psdm.rectangles.space(0.38, euclidian).polygons - psdm_cross_areaid_ce = psdm_space.edges.outside_part(areaid_ce).not(psdm_space.outside(areaid_ce).edges) - psdm_cross_areaid_ce.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, euclidian).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.not(areaid_ce) -if SEAL - ringLICON = licon.drc(with_holes > 0) - rectLICON = licon.not(ringLICON) -else - rectLICON = licon -end -rectLICON.non_rectangles.output("licon.1", "licon.1 : licon should be rectangle") -licon_peri.space(0.17, euclidian).output("licon.2", "licon.2: min. licon spacing in periphery : 0.17um") -licon.width(0.17, euclidian).output("licon.3", "licon.3: min. licon width : 0.17um") -licon_peri.and(difftap).separation(npc, 0.09, euclidian).output("licon.4", "licon.4: min. licon on diff spacing to npc in periphery : 0.09um") -licon_peri.and(difftap).and(npc).output("licon.4_a", "licon.4_a: licon on diff in periphery can't overlap npc") -licon.interacting(poly).and(licon.interacting(difftap)).output("licon.5", "licon.5 : 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)") + 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)") - -# 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)") -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") + 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 -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)") + 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 -# 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)") + 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)") - + 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") - + 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") + 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") + 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}")