mirror of
https://github.com/TinyTapeout/tt-support-tools.git
synced 2026-11-03 04:36:09 +01:00
feat(precheck): add zeroarea check
This commit is contained in:
@@ -87,6 +87,10 @@ def klayout_drc(gds: str, check: str, script=f"{PDK_NAME}_mr.drc"):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def klayout_zero_area(gds: str):
|
||||||
|
return klayout_drc(gds, "zero_area", "zeroarea.rb.drc")
|
||||||
|
|
||||||
|
|
||||||
def klayout_checks(gds: str):
|
def klayout_checks(gds: str):
|
||||||
layout = pya.Layout()
|
layout = pya.Layout()
|
||||||
layout.read(gds)
|
layout.read(gds)
|
||||||
@@ -134,6 +138,7 @@ def main():
|
|||||||
"pin_label_purposes_overlapping_drawing.rb.drc",
|
"pin_label_purposes_overlapping_drawing.rb.drc",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
["KLayout zero area", lambda: klayout_zero_area(args.gds)],
|
||||||
["KLayout Checks", lambda: klayout_checks(args.gds)],
|
["KLayout Checks", lambda: klayout_checks(args.gds)],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ drc_beol.xml
|
|||||||
drc_feol.xml
|
drc_feol.xml
|
||||||
drc_offgrid.xml
|
drc_offgrid.xml
|
||||||
drc_pin_label_purposes_overlapping_drawing.xml
|
drc_pin_label_purposes_overlapping_drawing.xml
|
||||||
|
drc_zero_area.xml
|
||||||
magic_drc.mag
|
magic_drc.mag
|
||||||
magic_drc.txt
|
magic_drc.txt
|
||||||
results.md
|
results.md
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
sky130A_mr.drc from https://github.com/efabless/mpw_precheck revision 4fd5283b124e931c9e71219f47270075176f84e2
|
sky130A_mr.drc from https://github.com/efabless/mpw_precheck revision 4fd5283b124e931c9e71219f47270075176f84e2
|
||||||
pin_label_purposes_overlapping_drawing.rb.drc from https://github.com/efabless/mpw_precheck revision f6b9c3d3f00694f96dce8444149449b4719180f0
|
pin_label_purposes_overlapping_drawing.rb.drc from https://github.com/efabless/mpw_precheck revision f6b9c3d3f00694f96dce8444149449b4719180f0
|
||||||
|
zeroarea.rb.drc from https://github.com/efabless/mpw_precheck revision 4fd5283b124e931c9e71219f47270075176f84e2
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# based in part on: https://www.klayout.de/forum/discussion/173/use-klayout-in-batch-mode
|
||||||
|
|
||||||
|
$errs = 0
|
||||||
|
del = 0
|
||||||
|
delZLp = true
|
||||||
|
len0 = 0
|
||||||
|
len0Del = 0
|
||||||
|
|
||||||
|
ly = RBA::Layout.new
|
||||||
|
source($input, $top_cell_name)
|
||||||
|
ly.read($input)
|
||||||
|
report("zero area check", $report)
|
||||||
|
|
||||||
|
ly.layer_indices.each { |li|
|
||||||
|
ly.each_cell { |cell|
|
||||||
|
cell.shapes(li).each { |shape|
|
||||||
|
# TODO: must we check that shape is one of: box, polygon, path? What happens to text?
|
||||||
|
if shape.is_valid? && (!shape.is_null?) &&
|
||||||
|
(shape.is_box? || shape.is_path? || shape.is_polygon?) &&
|
||||||
|
shape.area == 0
|
||||||
|
$errs += 1
|
||||||
|
polygons().insert(shape.dpolygon).output("zero_area_shape")
|
||||||
|
if $cleaned_output
|
||||||
|
shape.delete
|
||||||
|
del += 1
|
||||||
|
end
|
||||||
|
elsif delZLp && shape.is_valid? && (!shape.is_null?) && shape.is_path?
|
||||||
|
# puts "path on layer:#{layer_info} area:#{shape.area}"
|
||||||
|
lastpt = nil
|
||||||
|
same = true
|
||||||
|
# walk the points. First time we identify at least two unique-points, skip: not a zero-length path.
|
||||||
|
shape.each_point { |pt|
|
||||||
|
if lastpt && lastpt != pt
|
||||||
|
same = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
lastpt = pt
|
||||||
|
}
|
||||||
|
if same
|
||||||
|
# puts "zero-length path on layer:#{layer_info} area:#{shape.area} path:#{shape.to_s}"
|
||||||
|
len0 += 1
|
||||||
|
$errs += 1
|
||||||
|
if $cleaned_output
|
||||||
|
shape.delete
|
||||||
|
len0Del += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if $cleaned_output
|
||||||
|
puts "writing to #{$cleaned_output}"
|
||||||
|
ly.write($cleaned_output)
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "#{$errs} zero-area shapes"
|
||||||
|
|
||||||
|
del += len0Del
|
||||||
|
if delZLp
|
||||||
|
puts "#{len0} zero-length paths, #{len0Del} zero-length paths deleted."
|
||||||
|
end
|
||||||
|
puts "#{$errs} total zero-area objects, #{del} total objects deleted."
|
||||||
|
|
||||||
|
# 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: output-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
|
||||||
@@ -67,6 +67,20 @@ def gds_fail_metal5_poly(tmp_path_factory: pytest.TempPathFactory):
|
|||||||
return str(gds_file)
|
return str(gds_file)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def gds_zero_area(tmp_path_factory: pytest.TempPathFactory):
|
||||||
|
"""Creates a GDS with a zero-area polygon."""
|
||||||
|
gds_file = tmp_path_factory.mktemp("gds") / "gds_zero_area.gds"
|
||||||
|
layout = pya.Layout()
|
||||||
|
top_cell = layout.create_cell("TEST_zero_area")
|
||||||
|
met1_info = gds_layers["met1.drawing"]
|
||||||
|
met1 = layout.layer(met1_info.layer, met1_info.data_type)
|
||||||
|
rect = pya.DBox(0, 0, 0, 0)
|
||||||
|
top_cell.shapes(met1).insert(rect)
|
||||||
|
layout.write(str(gds_file))
|
||||||
|
return str(gds_file)
|
||||||
|
|
||||||
|
|
||||||
def test_magic_drc_pass(gds_empty: str):
|
def test_magic_drc_pass(gds_empty: str):
|
||||||
precheck.magic_drc(gds_empty, "TEST_empty")
|
precheck.magic_drc(gds_empty, "TEST_empty")
|
||||||
|
|
||||||
@@ -101,3 +115,12 @@ def test_klayout_checks_pass(gds_empty: str):
|
|||||||
def test_klayout_checks_fail(gds_fail_metal5_poly: str):
|
def test_klayout_checks_fail(gds_fail_metal5_poly: str):
|
||||||
with pytest.raises(precheck.PrecheckFailure):
|
with pytest.raises(precheck.PrecheckFailure):
|
||||||
precheck.klayout_checks(gds_fail_metal5_poly)
|
precheck.klayout_checks(gds_fail_metal5_poly)
|
||||||
|
|
||||||
|
|
||||||
|
def test_klayout_zero_area_drc_pass(gds_empty: str):
|
||||||
|
precheck.klayout_zero_area(gds_empty)
|
||||||
|
|
||||||
|
|
||||||
|
def test_klayout_zero_area_drc_fail(gds_zero_area: str):
|
||||||
|
with pytest.raises(precheck.PrecheckFailure):
|
||||||
|
precheck.klayout_zero_area(gds_zero_area)
|
||||||
|
|||||||
Reference in New Issue
Block a user