-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ci(precheck): experimental tapeout job
generates the final .oas and publishes it as an artifact
- Loading branch information
Showing
3 changed files
with
279 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import klayout.rdb as rdb | ||
import sys | ||
|
||
report_file = sys.argv[1] | ||
|
||
report = rdb.ReportDatabase("DRC") | ||
report.load(report_file) | ||
|
||
if report.num_items() > 0: | ||
raise RuntimeError(f"Klayout DRC failed with {report.num_items()} DRC violations") | ||
else: | ||
print("Klayout DRC passed") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
|
||
errs = 0 | ||
log("poly_density.drc:: sourcing design file=#{$gds_input} topcell=#{$top_cell} ...") | ||
src = source($gds_input, $top_cell) | ||
layout = src.layout | ||
log("done.") | ||
|
||
|
||
report("Density Checks", $report_file) | ||
|
||
verbose(false) | ||
|
||
diff_wildcard = "65/20" | ||
tap_wildcard = "65/44" | ||
polyfill_wildcard = "65,23/28" | ||
polymk_wildcard = "23/0" | ||
|
||
#$chip_boundary = input(235,4) | ||
log("flattening chip boundary...") | ||
$chip_boundary = input(235,4).flatten | ||
log("done.") | ||
|
||
$bbox = $chip_boundary.bbox | ||
|
||
window_size = 700 | ||
|
||
if $step | ||
step_size = $step.to_f | ||
else | ||
step_size = 70 | ||
end | ||
log("step size = #{step_size}") | ||
|
||
llx, lly, urx, ury = $bbox.left, $bbox.bottom, $bbox.right, $bbox.top | ||
log("llx=#{llx} lly=#{lly} urx=#{urx} ury=#{ury}") | ||
if urx-llx <= 0 || ury-lly <= 0 | ||
STDERR.puts "ERROR: poly_density.drc: fatal error, chip_boundary bbox empty or malformed" | ||
errs += 1 | ||
end | ||
|
||
cnt = 0 | ||
x_cnt = ((urx-step_size-llx) / step_size).ceil() | ||
y_cnt = ((ury-step_size-lly) / step_size ).ceil() | ||
tot = x_cnt * y_cnt | ||
$poly_area_map = array = Array.new(x_cnt+1) { Array.new(y_cnt+1) { 0.0 } } | ||
$tile_area_map = array = Array.new(x_cnt+1) { Array.new(y_cnt+1) { 0.0 } } | ||
|
||
log("x_cnt = #{x_cnt}") | ||
log("y_cnt = #{y_cnt}") | ||
|
||
if x_cnt < 1 || y_cnt < 1 | ||
STDERR.puts "ERROR: poly_density.drc: fatal error, x-count or y-count < 1: #{x_cnt} #{y_cnt}" | ||
errs += 1 | ||
end | ||
|
||
# any errors till now?: abort. | ||
if errs > 0 | ||
exit 1 | ||
end | ||
|
||
class PolyArea < RBA::TileOutputReceiver | ||
def put(ix, iy, tile, obj, dbu, clip) | ||
if tile | ||
poly_area_scaled = obj.to_f * dbu * dbu | ||
#tile_area_scaled = tile.area * dbu * dbu | ||
tile_area_scaled = (tile & ($bbox * (1.0 / dbu))).area * dbu * dbu | ||
#puts "got area for tile #{ix+1},#{iy+1}: #{poly_area_scaled.to_s} #{tile_area_scaled.to_s}" | ||
$poly_area_map[ix][iy] += poly_area_scaled | ||
$tile_area_map[ix][iy] = tile_area_scaled | ||
else | ||
puts "empty tile #{ix+1},#{iy+1}" | ||
end | ||
end | ||
def finish(success) | ||
puts "finish received: success = #{success}" | ||
end | ||
|
||
end | ||
|
||
tp = RBA::TilingProcessor::new | ||
|
||
# register the custom receiver | ||
tp.output("my_receiver", PolyArea::new) | ||
|
||
tp.input("poly", layout.top_cell().begin_shapes_rec(layout.layer(66,20) ) ) | ||
tp.input("polyfill", layout.top_cell().begin_shapes_rec(layout.layer(28,28) ) ) | ||
|
||
tp.frame=$bbox | ||
tp.tile_size(step_size, step_size) # 70x70 um tile size | ||
log("dbu = #{layout.dbu}") | ||
log("bbox_area = #{$bbox.area}") | ||
tp.dbu = layout.dbu | ||
tp.threads = $thr.to_i | ||
# tp.scale_to_dbu = false | ||
|
||
# The script clips the input at the tile and computes the (merged) area: | ||
tp.queue("_output(my_receiver, _tile && ((poly | polyfill) & _tile).area)") | ||
|
||
log("calculating subtile areas (= #{tot})...") | ||
tp.execute("Job description") | ||
|
||
cnt = 0 | ||
big_x_cnt = ((urx-window_size-llx) / step_size).ceil() | ||
big_y_cnt = ((ury-window_size-lly) / step_size).ceil() | ||
big_tot = big_x_cnt * big_y_cnt | ||
tiles_per_step = (window_size / step_size).ceil() | ||
log("tiles per step = #{tiles_per_step}") | ||
log("calculating window step densities (= #{big_tot})...") | ||
markers_min = polygon_layer | ||
markers_max = polygon_layer | ||
min_err = 0 | ||
max_err = 0 | ||
min_density = 1 | ||
max_density = 0 | ||
for x in (0 ... big_x_cnt) | ||
log("{{ CHECK }} #{cnt}/#{big_tot.round}") | ||
for y in (0 ... big_y_cnt) | ||
poly_area = 0 | ||
tile_area = 0 | ||
poly_density = 0 | ||
#$poly_area_map.slice(x, tiles_per_step).each { |a| a.slice(y, tiles_per_step).each { |b| puts "#{x},#{y} = #{b}" } } | ||
$poly_area_map.slice(x, tiles_per_step).each { |a| a.slice(y, tiles_per_step).each { |b| poly_area+=b } } | ||
$tile_area_map.slice(x, tiles_per_step).each { |a| a.slice(y, tiles_per_step).each { |b| tile_area+=b } } | ||
marker_box = box(x * step_size, y* step_size, (x+tiles_per_step)*step_size, (y+tiles_per_step)*step_size) | ||
if tile_area > 0 | ||
poly_density = poly_area / tile_area | ||
#log("poly density #{cnt}/#{big_tot.round} is #{poly_density} for area #{tile_area}") | ||
if poly_density < min_density | ||
min_density = poly_density | ||
end | ||
if poly_density > max_density | ||
max_density = poly_density | ||
end | ||
if poly_density < 0.08 | ||
min_err += 1 | ||
markers_min.insert(marker_box) | ||
log("poly density below 0.08 : #{cnt}/#{big_tot.round} is #{"%.4f" % poly_density} for tile (#{marker_box.to_s}), area = #{tile_area.round}") | ||
elsif poly_density > 0.38 | ||
max_err += 1 | ||
markers_max.insert(marker_box) | ||
log("poly density above 0.38 : #{cnt}/#{big_tot.round} is #{"%.4f" % poly_density} for tile (#{marker_box.to_s}), area = #{tile_area.round}") | ||
end | ||
end | ||
cnt += 1 | ||
end | ||
end | ||
|
||
log("minimum poly density = #{"%.4f" % min_density}") | ||
log("maximum poly density = #{"%.4f" % max_density}") | ||
|
||
if min_err > 0 | ||
log("cpoly.pd.1d violations = #{min_err}") | ||
markers_min.output("cpoly.pd.1d", "0.08 min poly pattern density") | ||
end | ||
|
||
if max_err > 0 | ||
log("cpoly.pd.1e violations = #{max_err}") | ||
markers_max.output("cpoly.pd.1e", "0.38 max poly pattern density") | ||
end |