blob: 7368b586d7c06d341d7ebf61f1c64b1b2a21d661 [file] [log] [blame]
# usage:
# klayout -r pin_label_purposes_overlapping_drawing <-rd threads=THREADS_NUM> <-rd tile=TILE_SIZE> <-rd flat_mode=true> <-rd check_pwell=true> <gdsFile> <topcellName> <markerReportOutFileName>
#
# Flag pin.not(drawing) for all known pin/label layers in sky130A.
# WARNING: if markerFile is RELATIVE-PATH it is written in SAME-DIR as input-GDS.
#
# pwell & pwelliso not checked by default. Believe labelling pwell without drawn pwell is legal.
#
# TODO?: are results correct if deep mode, for pin & drawing in different hier. levels?
#
# Exit status (does not work in klayout 0.23.11; does in 0.24 and later):
# 1 : I/O error or other internal error (uncaught exceptions).
# 2...127 : means 1... rules did flag error(s). If over 126 rules had errors, status is 127 max.
# That is this # is the how many rule-types had non-zero errors, NOT total errors count.
# 0 : no rules flagged errors.
# If process dies thru signal, exit status is 128+SIGNUM, so that range is reserved.
# i.e. if kernel oom-killer sends kill -9: status=137.
#
# Runs klayout (in batch) to do partial/crude layer grid checks; output to a MarkerDB (*.lyrdb)
# Crude because no partitioning is done, to enforce unique grid requirements by areaid.
#
# Script starts as regular ruby, then exec's via klayout passing self to it.
# (klayout requirement is this script-name *must* end in .drc).
#
# Known reasons why mult. klayout-versions produce non-identical output:
# 1. In some earlier versions (than 0.27), markerReport may state "wrong" generator:
# <generator>:/built-in-macros/drc.lym</generator>
# the "better" generator would look like:
# <generator>drc: script='<yourPath>/gdsSky130Apin1.drc'</generator>
# 2. When errors are flagged, the ordering of errors may differ between klayout versions.
#
# include RBA
begin
if $flat_mode == "true"
flat # flat, with or without tiling
else
deep
end
if $check_pwell == "true"
$check_pwell = true
else
$check_pwell = false
end
if !$threads || $threads.to_i == 0
$threads=`nproc`
end
if $tile
if $tile.to_f > 0
flat
tiles($tile)
puts "Tiling Enabled"
# no border
# tile_borders(0)
no_borders
end
end
threads($threads.to_i)
title = "pin_label_purposes_overlapping_drawing.rb.drc, input=#{$input}, topcell=#{$top_cell_name}"
puts "Running pin_label_purposes_overlapping_drawing.rb.drc on file=#{$input}, topcell=#{$top_cell_name}, output to #{$report}"
puts " deep:#{is_deep?} tiled:#{is_tiled?} threads:#{$threads}"
STDOUT.flush
source($input, $top_cell_name)
report(title, $report)
# $ruleHeader = "Checks with errors:"
$ruleHeader = "--- #err|description, table for cell: %s" % [$top_cell_name]
$didHeader = false
$errs = 0
$totals = 0
$rules = 0
#
# Direct rule checks like:
# m2.width(1.5).output("m2 width < 1.5")
# write to report() but don't give opportunity to count/print which rules did flag.
# Instead:
# rule(m2.width(1.5), "m2 width < 1.5")
#
# Wish to use direct-form, and just tabulate/post-process report after the fact.
# This form (empty or not) still does not nicely report/summarize error-count per-rule.
# Return value not meaningful if the is_empty? fails (in 0.26.9 or earlier).
#
def rule(marker, msg)
$rules = $rules + 1
empty = false
size = -1
emptyFails = false
# test if marker is empty.
# marker.is_empty? : works in 0.27, not 0.26 and earlier.
# marker.size : does not work in any so far.
# marker.bbox appears universal, works in klayout versions 0.23.11 ... 0.27.
# In case it fails, catch exception and revert to less information in our stdout.
begin
size = marker.data.size
empty = (size == 0)
# works all versions: size = marker.data.size
# works all versions: empty = marker.bbox.to_s == "()"
# fails pre 0.27: empty = marker.is_empty?
rescue StandardError => e
# when can't determine which rules flagged errors, signal not to summarize.
emptyFails = true
empty = false
size = -1
if $errs >= 0
$errs = -8
end
if ($errs & 1) == 0
puts "turned off marker empty detect..."
end
$errs = $errs | 1
end
if ! empty
marker.output(msg)
if ! emptyFails
if $errs == 0 && ! $didHeader
puts $ruleHeader
$didHeader = true
end
$errs = $errs + 1
$totals = $totals + size
puts "%8d %s" % [size, msg]
return 1
end
end
return 0
end
# call like:
#
# pinCheck( "met1", 68,20, 68,16, 68, 5 )
#
# where 68,20 is met1/drawing, and purposes 16 & 5 are pin,label
#
# It is an error if called:
# ... with less than 5 args, i.e. MUST have at least one non-drawing layer-purpose-pair.
# e.g.: pinCheck( "met1", 68,20 )
# ... with an odd number of arguments after the first three.
# e.g.: pinCheck( "met1", 68,20, 68 )
# e.g.: pinCheck( "met1", 68,20, 68,16, 68 )
#
# Variations:
# pinCheck: do the regular AndNot pin check
# pinSkip: do not do the check, but still report on which lpps have data vs empty.
def pinCheck(name, layn, typen, *nonMaskLpps)
pinCheckIf(true, name, layn, typen, *nonMaskLpps)
end
def pinSkip(name, layn, typen, *nonMaskLpps)
pinCheckIf(false, name, layn, typen, *nonMaskLpps)
end
def pinCheckIf(checkp, name, layn, typen, *nonMaskLpps)
if nonMaskLpps.length == 0 || nonMaskLpps.length % 2 != 0
STDERR.puts "pinCheck called with empty or odd-number of args for non-drawing layer-purpose-pairs."
exit 1
end
lyfmt = "%22s"
lyfmt2 = "%12s"
ly = polygons(layn, typen) # get main drawing
drpair = "#{layn}/#{typen}"
# isEmpty(ly, name)
lye = (ly.is_empty?) ? "EMP" : "dat"
sumry = [ lyfmt % "#{name}:#{drpair}/#{lye}" ]
nonMaskLpps.each_slice(2) {|lay, typ|
l2 = polygons(lay, typ)
l2e = (l2.is_empty?) ? "EMP" : "dat"
sumry += [ lyfmt2 % "#{lay}/#{typ}/#{l2e}" ]
msg = "#{lay}/#{typ}: #{name}, pin/label not-over drawing:#{drpair}"
if checkp
rule( l2.not(ly), msg )
end
}
if checkp
mode = " "
else
mode = "NO-Check"
end
# force header output (if not yet done)
if ! $didHeader
puts $ruleHeader
$didHeader = true
end
puts ("%8s ---- " % mode) + sumry.join(" ")
end
# verbose input(), flag if its empty. description is a string.
def inputVerb(layn, typen, desc)
ly = input(layn, typen)
isEmpty(ly, desc)
return ly
end
def isEmpty(layer, desc)
if layer.is_empty?
puts "--EMPTY : #{desc}"
else
puts "data : #{desc}"
end
end
# check all layer-purpose-pairs found in input-layout:
# Report ALL that are pin-purpose.
#? should these be checked against pwell/drawing: pwelliso_pin - 44/16 pwelliso_label - 44/5
pinCheckIf($check_pwell,
"pwell", 64,44, 122,16, 64,59, 44,16, 44,5)
pinCheck( "nwell", 64,20, 64,16, 64,5)
pinCheck( "diff", 65,20, 65,16, 65,6)
pinCheck( "tap", 65,44, 65,48, 65,5)
pinCheck( "poly", 66,20, 66,16, 66,5)
pinCheck( "licon1", 66,44, 66,58)
pinCheck( "li1", 67,20, 67,16, 67,5)
pinCheck( "mcon", 67,44, 67,48)
pinCheck( "met1", 68,20, 68,16, 68,5)
pinCheck( "via", 68,44, 68,58)
pinCheck( "met2", 69,20, 69,16, 69,5)
pinCheck( "via2", 69,44, 69,58)
pinCheck( "met3", 70,20, 70,16, 70,5)
pinCheck( "via3", 70,44, 70,48)
pinCheck( "met4", 71,20, 71,16, 71,5)
pinCheck( "via4", 71,44, 71,48)
pinCheck( "met5", 72,20, 72,16, 72,5)
pinCheck( "pad", 76,20, 76,5, 76,16)
pinCheck( "pnp", 82,44, 82,59)
pinCheck( "npn", 82,20, 82,5)
pinCheck( "rdl", 74,20, 74,16, 74,5)
pinCheck( "inductor", 82,24, 82,25)
end
# How to tabulate as-if-flat "error-counts by error-message" from current report()?
# In old versions, we can't seem to determine here if a check flagged errors ($errs == -1).
if $errs >= 0
puts "%8d total error(s) among %d error type(s), %d checks, cell: %s" % [$totals, $errs, $rules, $top_cell_name]
# puts "#{$errs} of #{$rules} checks have errors"
else
puts "#{$rules} checks"
$errs = 0 # so exit status == 0.
end
puts "Writing report..."
# if we roll-over to 256, exit-status seen by shell is zero.
# uncaught I/O errors will yield (built-in) exit status of 1.
if $errs > 0
$errs = $errs + 1
end
if $errs > 127
$errs = 127
end
# experimental: report own peak process-stats. BUT: report-file isn't really written
# until we exit (during exit). So these results are not 100% accurate.
# VmHWM: max-resident-size, VmPeak: max virtual-size.
# don't need: pid=Process.pid
if File.readable?("/proc/self/status")
puts File.foreach("/proc/self/status").grep(/^(VmPeak|VmHWM)/)
end
# does not work (to set exit-status) in 0.23.11.
# Does work in 0.24.2, 0.27!
exit $errs
# For 0.23.11 we could set exit-status as below, but:
# if we do: the report() does not get written!
#
# for exit! we'd lose buffered output unless we flush:
# STDOUT.flush
# STDERR.flush
# Kernel.exit!($errs)
#
# emacs syntax-mode:
# Local Variables:
# mode:ruby
# End: