diff --git a/precheck/precheck.py b/precheck/precheck.py index eb06c90..a71a60a 100644 --- a/precheck/precheck.py +++ b/precheck/precheck.py @@ -113,7 +113,11 @@ def klayout_zero_area(gds: str): def klayout_sg13g2(gds: str): - return klayout_drc(gds, "sg13g2", "sg13g2_mr.lydrc", extra_vars=[f"in_gds={gds}"]) + return klayout_drc( + gds, + "sg13g2", + f"{PDK_ROOT}/{PDK_NAME}/libs.tech/klayout/tech/drc/ihp-sg13g2.drc", + ) def klayout_checks(gds: str, expected_name: str, tech: str): diff --git a/precheck/tech-files/sg13g2_mr.lydrc b/precheck/tech-files/sg13g2_mr.lydrc deleted file mode 100644 index 687e99d..0000000 --- a/precheck/tech-files/sg13g2_mr.lydrc +++ /dev/null @@ -1,1052 +0,0 @@ - - - - - - drc - - - - false - false - - true - drc_scripts - tools_menu.drc.end - dsl - drc-dsl-xml - # Supported variables that can be set using "-rd <name>=<value>" on the command line: -# log_file - path to the log file [default: no log file] -# in_gds - path to the GDS layout to check (required in batch mode) -# cell - name of the cell to check -# report_file - path to the report database [default: sg13g2_minimal.lyrdb in the script directory] - -# to set logfile: -rd logfile="sg13g2_minimal.log" -if $log_file - log_file($log_file) -end - -application = RBA::Application.instance -main_window = application.main_window -if main_window - curr_layout_view = main_window.current_view() - unless curr_layout_view - layout_path = RBA::FileDialog::ask_open_file_name("Chose your layout file.", ".", "GDSII files (*.GDS *.gds *.GDS.gz *.gds.gz *.GDS2 *.gds2 *.GDS2.gz *.gds2.gz);; All files (*)") - main_window.load_layout(layout_path, 1) - curr_layout_view = main_window.current_view() - end - active_layout = RBA::CellView::active.layout - active_cellname = RBA::CellView::active.cell_name - source(active_layout, active_cellname) -else - log("DRC: batch mode") - # to set input layout: -rd in_gds="path to GDS file" - # to set cell: -rd cell="topcell" - if $cell - active_cellname = $cell - log("Active cell: " + active_cellname) - source($in_gds, active_cellname) - active_layout = source.layout - else - source($in_gds) - active_layout = source.layout - active_cellname = source.cell_name - end -end - -unless active_layout or $in_gds - raise("layout file must be defined on command line or via 'in_gds' script variable") -end - -if active_layout.dbu != 0.001 - puts "WARNING: Layout dbu value (" + active_layout.dbu.to_s + " ) deviates from rule file dbu value (0.001). This will scale the layout and may not be intended." -end - -report_file = __dir__ + "/sg13g2_minimal.lyrdb" -# to set report file: -rd report_file="sg13g2_minimal.lyrdb" -if $report_file - report_file = File.expand_path($report_file) -end - -report("design rules: sg13g2_minimal | layout cell: " + active_cellname, report_file) - -deep - -$drc_error_count = 0 - -class DRC::DRCLayer - unless method_defined?(:original_output) - alias_method :original_output, :output - end - - def output(*args) - $drc_error_count += self.count() - original_output(*args) - end - - def ext_merged(min_coherence, overlap_count) - args = [min_coherence, overlap_count] - return DRCLayer::new(@engine, @engine._tcmd(self.data, 0, self.data.class, :merged, *args)) - end - - def ext_merge(min_coherence, overlap_count) - args = [min_coherence, overlap_count] - if @engine.is_tiled? - self.data = @engine._tcmd(self.data, 0, self.data.class, :merged, *args) - else - @engine._tcmd(self.data, 0, self.data.class, :merge, *args) - end - return self - end -end - -# Initial definitions of control flow variables -# Strings from the command line have to be converted -if defined? $density - $density = $density.to_s.downcase == "true" -else - $density = true -end -if defined? $sanityRules - $sanityRules = $sanityRules.to_s.downcase == "true" -else - $sanityRules = true -end - -class DRC::DRCEngine - def space_width_separation_overlap_check(value, - error_edge_pairs_90, - error_edge_pairs_180, - inverse_error_edge_pairs_90 = nil, - inverse_error_edge_pairs_180 = nil, - options = {}) - consider_intersecting_edges = options.fetch(:consider_intersecting_edges, false) - consider_touch_points = options.fetch(:consider_touch_points, false) - ignore_non_axis_aligned_edges = options.fetch(:ignore_non_axis_aligned_edges, false) - min_angle = options.fetch(:min_angle, 0) - max_angle = options.fetch(:max_angle, 90) - include_min_angle = options.fetch(:include_min_angle, true) - include_max_angle = options.fetch(:include_max_angle, false) - area_of_right_angle = value**2/2 - errors_ep = RBA::EdgePairs::new() - touch_point_errors_ep = RBA::EdgePairs::new() - intersecting_edges_errors_ep = RBA::EdgePairs::new() - intersecting_edges_error_candidates = Hash.new() - no_touch_point_error = Hash.new() - error_edge_pairs_90.data.each do |edge_pair| - ip = nil - if edge_pair.first.p1 == edge_pair.second.p1 or - edge_pair.first.p1 == edge_pair.second.p2 - ip = edge_pair.first.p1 - elsif edge_pair.first.p2 == edge_pair.second.p1 or - edge_pair.first.p2 == edge_pair.second.p2 - ip = edge_pair.first.p2 - end - if ip - intersecting_edges_error_candidates[ip] = edge_pair - if !edge_pair.first.is_degenerate? and !edge_pair.second.is_degenerate? - if (edge_pair.first.contains?(edge_pair.second.p1) and - edge_pair.first.contains?(edge_pair.second.p2)) or - (edge_pair.second.contains?(edge_pair.first.p1) and - edge_pair.second.contains?(edge_pair.first.p2)) - no_touch_point_error[ip] = true - end - end - else - errors_ep.insert(edge_pair) - end - end - if consider_intersecting_edges or consider_touch_points - touch_point_candidates = Hash.new() - touch_point_errors = Hash.new() - if inverse_error_edge_pairs_90 and inverse_error_edge_pairs_180 - (inverse_error_edge_pairs_90 + inverse_error_edge_pairs_180).data.each do |edge_pair| - ip = edge_pair.first.intersection_point(edge_pair.second) - if ip - if touch_point_candidates[ip] - touch_point_errors[ip] = true - touch_point_candidates.delete(ip) - elsif !no_touch_point_error[ip] - touch_point_candidates[ip] = edge_pair - end - end - end - end - touch_point_candidates = Hash.new() - (error_edge_pairs_90 + error_edge_pairs_180).data.each do |edge_pair| - ip = nil - if edge_pair.first.p1 == edge_pair.second.p1 or - edge_pair.first.p1 == edge_pair.second.p2 - ip = edge_pair.first.p1 - elsif edge_pair.first.p2 == edge_pair.second.p1 or - edge_pair.first.p2 == edge_pair.second.p2 - ip = edge_pair.first.p2 - end - if ip - if edge_pair.area == area_of_right_angle or max_angle == 180 - intersecting_edges_error_candidates[ip] = edge_pair - end - if touch_point_errors[ip] - touch_point_errors_ep.insert(edge_pair) - intersecting_edges_error_candidates.delete(ip) - elsif touch_point_candidates[ip] - touch_point_errors_ep.insert(edge_pair) - touch_point_errors_ep.insert(touch_point_candidates[ip]) - touch_point_candidates.delete(ip) - intersecting_edges_error_candidates.delete(ip) - elsif !no_touch_point_error[ip] - touch_point_candidates[ip] = edge_pair - end - end - end - if consider_intersecting_edges - intersecting_edges_errors_ep = RBA::EdgePairs::new(intersecting_edges_error_candidates.values) - if max_angle != 180 - intersecting_edges_errors_ep = intersecting_edges_errors_ep.with_internal_angle(min_angle, max_angle, false, include_min_angle, include_max_angle) - end - errors_ep = errors_ep + intersecting_edges_errors_ep - end - end - if ignore_non_axis_aligned_edges - errors_ep = errors_ep.with_angle_both(RBA::Edges::OrthoEdges, false) - end - if consider_touch_points - errors_ep = errors_ep + touch_point_errors_ep - end - return DRC::DRCLayer::new(self, errors_ep) - end -end - -class DRC::DRCLayer - def ext_and(other) - self_min_coherence_state = self.data.min_coherence? - other_min_coherence_state = other.data.min_coherence? - self.data.min_coherence = true - other.data.min_coherence = true - output_layer = self & other - self.data.min_coherence = self_min_coherence_state - other.data.min_coherence = other_min_coherence_state - return output_layer - end - - def ext_with_area(constraint) - lower_bound = nil - upper_bound = nil - output_layer = nil - self_min_coherence_state = self.data.min_coherence? - self.data.min_coherence = true - constraint.each do |expression| - relation = expression[0] - value = expression[1] - if relation == ">" - lower_bound = value + 1e-6 - elsif relation == "<" - upper_bound = value - elsif relation == "==" - output_layer = self.with_area(value) - elsif relation == "!=" - output_layer = self.without_area(value) - elsif relation == ">=" - lower_bound = value - elsif relation == "<=" - upper_bound = value + 1e-6 - else - raise "invalid expression" - end - end - if lower_bound or upper_bound - output_layer = self.with_area(lower_bound, upper_bound) - end - self.data.min_coherence = self_min_coherence_state - return output_layer - end - - def ext_constraint_satisfied(value, constraint) - output_bool = true - constraint.each do |expression| - if expression[0] == ">" - output_bool = output_bool && (value > expression[1]) - elsif expression[0] == "<" - output_bool = output_bool && (value < expression[1]) - elsif expression[0] == "==" - output_bool = output_bool && (value == expression[1]) - elsif expression[0] == "!=" - output_bool = output_bool && (value != expression[1]) - elsif expression[0] == ">=" - output_bool = output_bool && (value >= expression[1]) - elsif expression[0] == "<=" - output_bool = output_bool && (value <= expression[1]) - else - raise "invalid expression" - end - end - return output_bool - end - - def ext_covering(other) - self_min_coherence_state = self.data.min_coherence? - other_min_coherence_state = other.data.min_coherence? - self.data.min_coherence = true - other.data.min_coherence = true - output_layer = self.covering(other.inside(self)) - self.data.min_coherence = self_min_coherence_state - other.data.min_coherence = other_min_coherence_state - return output_layer - end - - def ext_separation(other, - value, - metric=RBA::Region::Euclidian, - consider_intersecting_edges=false, - consider_touch_points=false, - ignore_non_axis_aligned_edges=false, - min_angle=0, - max_angle=90, - include_min_angle=true, - include_max_angle=false, - polygons=false) - self_min_coherence_state = self.data.min_coherence? - other_min_coherence_state = other.data.min_coherence? - self.data.min_coherence = true - other.data.min_coherence = true - if metric.is_a?(DRC::DRCMetrics) - metric = metric.value - end - error_edge_pairs_90 = DRC::DRCLayer::new(@engine, - self.data.separation_check(other.data, value, false, metric, 90, 1, nil)) - error_edge_pairs_180 = DRC::DRCLayer::new(@engine, - self.data.separation_check(other.data, value, false, metric, 180, nil, 1)) - width_error_edge_pairs_90 = DRC::DRCLayer::new(@engine, - self.data.width_check(value, false, metric, 90, 1, nil) + - other.data.width_check(value, false, metric, 90, 1, nil)) - width_error_edge_pairs_180 = DRC::DRCLayer::new(@engine, - self.data.width_check(value, false, metric, 180, nil, 1) + - other.data.width_check(value, false, metric, 180, nil, 1)) - separation_errors = @engine.space_width_separation_overlap_check( - value, - error_edge_pairs_90, - error_edge_pairs_180, - width_error_edge_pairs_90, - width_error_edge_pairs_180, - { - consider_intersecting_edges: consider_intersecting_edges, - consider_touch_points: consider_touch_points, - ignore_non_axis_aligned_edges: ignore_non_axis_aligned_edges, - min_angle: min_angle, - max_angle: max_angle, - include_min_angle: include_min_angle, - include_max_angle: include_max_angle - } - ) - self.data.min_coherence = self_min_coherence_state - other.data.min_coherence = other_min_coherence_state - if polygons - return separation_errors.polygons.merge(true, 0) - else - return separation_errors - end - end - - def ext_fast_separation(other, - value, - metric: @engine.euclidian, - consider_intersecting_edges: true, - consider_touch_points: true, - ignore_non_axis_aligned_edges: false, - min_angle: 0, - max_angle: 90, - include_min_angle: true, - include_max_angle: false, - polygon_output: false) - self_min_coherence_state = self.data.min_coherence? - other_min_coherence_state = other.data.min_coherence? - self.data.min_coherence = true - other.data.min_coherence = true - output_layer = self.separation(other, value, metric, @engine.angle_limit(max_angle)) - if !consider_intersecting_edges and !consider_touch_points - output_layer = output_layer.with_distance(1, nil) - if ignore_non_axis_aligned_edges - output_layer = output_layer.with_angle(@engine.ortho, @engine.both) - end - elsif consider_intersecting_edges ^ consider_touch_points - candidate_layer1 = self.interacting(output_layer.edges) - candidate_layer2 = other.interacting(output_layer.edges) - output_layer = candidate_layer1.ext_separation( - candidate_layer2, - value, - metric, - consider_intersecting_edges, - consider_touch_points, - ignore_non_axis_aligned_edges, - min_angle, - max_angle, - include_min_angle, - include_max_angle, - false) - end - self.data.min_coherence = self_min_coherence_state - other.data.min_coherence = other_min_coherence_state - if polygon_output - return output_layer.polygons.merge(true, 0) - else - return output_layer - end - end - - def ext_not(other) - self_min_coherence_state = self.data.min_coherence? - other_min_coherence_state = other.data.min_coherence? - self.data.min_coherence = true - other.data.min_coherence = true - output_layer = self - other - self.data.min_coherence = self_min_coherence_state - other.data.min_coherence = other_min_coherence_state - return output_layer - end - - def ext_or(other, *further_layers) - self_min_coherence_state = self.data.min_coherence? - other_min_coherence_state = other.data.min_coherence? - self.data.min_coherence = true - other.data.min_coherence = true - output_layer = self.join(other) - further_layers.each do |further_layer| - further_layer_min_coherence_state = further_layer.data.min_coherence? - further_layer.data.min_coherence = true - output_layer = output_layer.join(further_layer) - further_layer.data.min_coherence = further_layer_min_coherence_state - end - self.data.min_coherence = self_min_coherence_state - other.data.min_coherence = other_min_coherence_state - return output_layer - end - - def ext_rectangles(axis_aligned = false, use_bbox = false, constraint1 = nil, constraint2 = nil, aspect_ratio_constraint = nil, inverted: false) - self_min_coherence_state = self.data.min_coherence? - self.data.min_coherence = true - if ( ( constraint1 && ( !constraint2 || constraint1.length() > 1 || constraint1[0][0] != "==") ) || - ( constraint2 && ( constraint2.length() > 1 || constraint2[0][0] != "==" ) ) || - ( constraint1 && constraint2 && constraint1[0][1] != constraint2[0][1] ) ) - raise "ext_rectangle: unsupported options" - end - square = constraint1 ? true : false - shape_filter = - if use_bbox - @engine.extents - elsif axis_aligned - @engine.rectangles - else - @engine.if_all((@engine.corners == 270).count == 4, @engine.corners.count == 4) - end - if square - if use_bbox - shape_filter = @engine.if_all((@engine.extents.length == constraint1[0][1]).count == 4) - else - square_filter = (@engine.length == constraint1[0][1]).count == 4 - shape_filter = @engine.if_all(shape_filter, square_filter) - end - end - if inverted - output_layer = self.drc(! shape_filter) - else - output_layer = self.drc(shape_filter) - end - self.data.min_coherence = self_min_coherence_state - return output_layer - end - - def ext_space(value, - metric=RBA::Region::Euclidian, - consider_intersecting_edges=false, - consider_touch_points=false, - ignore_non_axis_aligned_edges=false, - min_angle=0, - max_angle=90, - include_min_angle=true, - include_max_angle=false, - polygons=false) - self_min_coherence_state = self.data.min_coherence? - self.data.min_coherence = true - if metric.is_a?(DRC::DRCMetrics) - metric = metric.value - end - error_edge_pairs_90 = DRC::DRCLayer::new(@engine, self.data.space_check(value, false, metric, 90, 1, nil)) - error_edge_pairs_180 = DRC::DRCLayer::new(@engine, self.data.space_check(value, false, metric, 180, nil, 1)) - width_error_edge_pairs_90 = DRC::DRCLayer::new(@engine, self.data.width_check(value, false, metric, 90, 1, nil)) - width_error_edge_pairs_180 = DRC::DRCLayer::new(@engine, self.data.width_check(value, false, metric, 180, nil, 1)) - space_errors = @engine.space_width_separation_overlap_check( - value, - error_edge_pairs_90, - error_edge_pairs_180, - width_error_edge_pairs_90, - width_error_edge_pairs_180, - { - consider_intersecting_edges: consider_intersecting_edges, - consider_touch_points: consider_touch_points, - ignore_non_axis_aligned_edges: ignore_non_axis_aligned_edges, - min_angle: min_angle, - max_angle: max_angle, - include_min_angle: include_min_angle, - include_max_angle: include_max_angle - } - ) - self.data.min_coherence = self_min_coherence_state - if polygons - return space_errors.polygons.merge(true, 0) - else - return space_errors - end - end - - def ext_fast_space(value, - metric: @engine.euclidian, - consider_intersecting_edges: true, - consider_touch_points: true, - ignore_non_axis_aligned_edges: false, - min_angle: 0, - max_angle: 90, - include_min_angle: true, - include_max_angle: false, - polygon_output: false) - self_min_coherence_state = self.data.min_coherence? - self.data.min_coherence = true - output_layer = self.space(value, metric, @engine.angle_limit(max_angle)) - if !consider_intersecting_edges and !consider_touch_points - output_layer = output_layer.with_distance(1, nil) - if ignore_non_axis_aligned_edges - output_layer = output_layer.with_angle(@engine.ortho, @engine.both) - end - elsif consider_intersecting_edges ^ consider_touch_points - candidate_layer = self.interacting(output_layer.edges) - output_layer = candidate_layer.ext_space( - value, - metric, - consider_intersecting_edges, - consider_touch_points, - ignore_non_axis_aligned_edges, - min_angle, - max_angle, - include_min_angle, - include_max_angle, - false) - end - self.data.min_coherence = self_min_coherence_state - if polygon_output - return output_layer.polygons.ext_merge(true, 0) - else - return output_layer - end - end - - def ext_fast_width(value, polygon_output: false) - self_min_coherence_state = self.data.min_coherence? - self.data.min_coherence = true - output_layer = self.width(value) - self.data.min_coherence = self_min_coherence_state - if polygon_output - return output_layer.polygons.ext_merge(true, 0) - else - return output_layer - end - end - - def ext_interacting_with_text(text_layer, text) - if text_layer.is_a? Integer - text_layer = @engine.labels(text_layer) - end - initial_merged_semantics = self.data.merged_semantics? - self.data.merged_semantics = false - result = self.interacting(text_layer.texts(text)) - self.data.merged_semantics = initial_merged_semantics - return result - end - - def ext_with_density(range, *args) - if self.is_empty? - return DRC::DRCLayer::new(@engine, RBA::Region::new()) - end - if self.is_merged? - merged_layer = self - else - merged_layer = self.ext_merged(true, 0) - end - origin = 'cc' - tile_size = nil - tile_step = nil - arguments = [range] - args.each do |a| - if a.is_a?(DRC::DRCTileSize) - tile_size = a - arguments.push(tile_size) - elsif a.is_a?(DRC::DRCTileStep) - tile_step = a - arguments.push(tile_step) - elsif a.is_a?(String) - origin = a - else - raise "argument error" - end - end - bbox = @engine.extent.bbox - if origin == 'll' - origin_x = bbox.left - origin_y = bbox.bottom - if tile_size and tile_step and (tile_size.get[0] != tile_step.get[0] or tile_size.get[1] != tile_step.get[1]) - origin_x = bbox.left + tile_step.get[0]/2 - origin_y = bbox.bottom + tile_step.get[1]/2 - end - tile_origin = DRC::DRCTileOrigin::new(origin_x, origin_y) - arguments.push(tile_origin) - elsif origin != 'cc' - raise "Unknown origin: 'cc' or 'll' expected" - end - if tile_size - boundary_layer = DRC::DRCLayer::new(@engine, RBA::Region::new(bbox.to_itype(@engine.dbu))) - tile_boundary = DRC::DRCTileBoundary::new(boundary_layer) - return merged_layer.with_density(*arguments, tile_boundary, @engine.padding_ignore) - else - tile_size = DRC::DRCTileSize::new(bbox.width, bbox.height) - tile_count = DRC::DRCTileCount::new(1,2) - enlarged_bbox = bbox.enlarged(1.1).to_itype(@engine.dbu) - boundary_layer = DRC::DRCLayer::new(@engine, RBA::Region::new(enlarged_bbox)) - tile_boundary = DRC::DRCTileBoundary::new(boundary_layer) - result = merged_layer.with_density(*arguments, tile_size, tile_count, tile_boundary, @engine.padding_ignore) - return result.raw.overlapping(DRC::DRCLayer::new(@engine, RBA::Region::new(bbox.to_itype(@engine.dbu)))) - end - end -end - -Activ = source.polygons("1/0") -Activ_pin = source.polygons("1/2") -Activ_filler = source.polygons("1/22") -BiWind = source.polygons("3/0") -GatPoly = source.polygons("5/0") -GatPoly_pin = source.polygons("5/2") -GatPoly_filler = source.polygons("5/22") -Cont = source.polygons("6/0") -Metal1 = source.polygons("8/0") -Metal1_pin = source.polygons("8/2") -Metal1_filler = source.polygons("8/22") -Metal1_slit = source.polygons("8/24") -Passiv = source.polygons("9/0") -Metal2 = source.polygons("10/0") -Metal2_pin = source.polygons("10/2") -Metal2_filler = source.polygons("10/22") -Metal2_slit = source.polygons("10/24") -BasPoly = source.polygons("13/0") -Via1 = source.polygons("19/0") -SRAM = source.polygons("25/0") -TRANS = source.polygons("26/0") -Via2 = source.polygons("29/0") -Metal3 = source.polygons("30/0") -Metal3_pin = source.polygons("30/2") -Metal3_filler = source.polygons("30/22") -Metal3_slit = source.polygons("30/24") -DeepCo = source.polygons("35/0") -EdgeSeal = source.polygons("39/0") -ThickGateOx = source.polygons("44/0") -Via3 = source.polygons("49/0") -Metal4 = source.polygons("50/0") -Metal4_pin = source.polygons("50/2") -Metal4_filler = source.polygons("50/22") -Metal4_slit = source.polygons("50/24") -EmPoly = source.polygons("55/0") -TEXT_0 = source.labels("63/0") -Via4 = source.polygons("66/0") -Metal5 = source.polygons("67/0") -Metal5_pin = source.polygons("67/2") -Metal5_filler = source.polygons("67/22") -Metal5_slit = source.polygons("67/24") -TopVia1 = source.polygons("125/0") -TopMetal1 = source.polygons("126/0") -TopMetal1_pin = source.polygons("126/2") -TopMetal1_filler = source.polygons("126/22") -TopMetal1_slit = source.polygons("126/24") -Vmim = source.polygons("129/0") -TopVia2 = source.polygons("133/0") -TopMetal2 = source.polygons("134/0") -TopMetal2_pin = source.polygons("134/2") -TopMetal2_filler = source.polygons("134/22") -TopMetal2_slit = source.polygons("134/24") -ColWind = source.polygons("139/0") -LBE = source.polygons("157/0") - -if $sanityRules - PEmWind = source.polygons("11/0") - PEmPoly = source.polygons("53/0") - LDMOS = source.polygons("57/0") - PBiWind = source.polygons("58/0") - Flash = source.polygons("71/0") -end - -Activ_Act_a = Activ.ext_fast_width(0.15.um) -Act_density = Activ.ext_or(Activ_filler) -Gat_density = GatPoly.ext_or(GatPoly_filler) -Cont_SQ = Cont.ext_rectangles(true, false, [["==", 0.16.um]], [["==", 0.16.um]], nil) -ContBar = Cont.ext_with_area([[">", (0.16*0.16).um2]]) -Act_Nsram = Activ.ext_not(SRAM) -GP_Nsram = GatPoly.ext_not(SRAM) -M1_Nsram = Metal1.ext_not(SRAM) -M2_Nsram = Metal2.ext_not(SRAM) -M3_Nsram = Metal3.ext_not(SRAM) -Via1_edgC1_out = Via1.ext_not(EdgeSeal) -Via2_edgC1_out = Via2.ext_not(EdgeSeal) -Cont_outside_EdgeSeal = Cont.outside(EdgeSeal) -ThickGateOx_TGO_f = ThickGateOx.ext_fast_width(0.86.um, polygon_output: true) -Via3_edgC1_out = Via3.ext_not(EdgeSeal) -M4_Nsram = Metal4.ext_not(SRAM) -Via4_edgC1_out = Via4.ext_not(EdgeSeal) -M5_Nsram = Metal5.ext_not(SRAM) -TopVia1_edgC1_out = TopVia1.ext_not(EdgeSeal) -TopVia1_or_Vmim = TopVia1.ext_or(Vmim) -TopVia2_edgC1_out = TopVia2.ext_not(EdgeSeal) -M1_density = Metal1.ext_or(Metal1_filler).ext_not(Metal1_slit) -M2_density = Metal2.ext_or(Metal2_filler).ext_not(Metal2_slit) -emi2Pin = Metal2_pin.ext_and(TRANS).ext_interacting_with_text(TEXT_0, "E") -M3_density = Metal3.ext_or(Metal3_filler).ext_not(Metal3_slit) -M4_density = Metal4.ext_or(Metal4_filler).ext_not(Metal4_slit) -M5_density = Metal5.ext_or(Metal5_filler).ext_not(Metal5_slit) -TM1_density = TopMetal1.ext_or(TopMetal1_filler).ext_not(TopMetal1_slit) -TM2_density = TopMetal2.ext_or(TopMetal2_filler).ext_not(TopMetal2_slit) -GP_Nsram_Gat_a = GP_Nsram.ext_fast_width(0.13.um, polygon_output: true) -GP_Nsram_Gat_b = GP_Nsram.ext_fast_space(0.18.um, consider_intersecting_edges: false, polygon_output: true) -transG2L = TRANS.ext_interacting_with_text(TEXT_0, "npn13G2L").ext_covering(emi2Pin) --> do - Activ_Act_a.dup -end.().output("Act.a", "Min. Activ width = 0.15") --> do - Act_Nsram.ext_fast_space(0.21.um) -end.().output("Act.b", "Min. Activ space or notch = 0.21") - -if $density - #-> do - # Act_density.ext_with_density(0.0 .. 0.35, 'll') - #end.().output("AFil.g", "Min. global Activ density [%] = 35.00") - -> do - Act_density.ext_with_density(0.55 .. 1.0, 'll') - end.().output("AFil.g1", "Max. global Activ density [%] = 55.00") - #-> do - # Act_density.ext_with_density(0.0 .. 0.25, 'll', tile_size(800.0.um), tile_step(400.0.um)) - #end.().output("AFil.g2", "Min. Activ coverage ratio for any 800 x 800 µm² chip area [%] = 25.00") - -> do - Act_density.ext_with_density(0.65 .. 1.0, 'll', tile_size(800.0.um), tile_step(400.0.um)) - end.().output("AFil.g3", "Max. Activ coverage ratio for any 800 x 800 µm² chip area [%] = 65.00") -end - --> do - ThickGateOx_TGO_f.dup -end.().output("TGO.f", "Min. ThickGateOx width = 0.86") --> do - GP_Nsram_Gat_a.dup -end.().output("Gat.a", "Min. GatPoly width = 0.13") --> do - GP_Nsram_Gat_b.dup -end.().output("Gat.b", "Min. GatPoly space or notch = 0.18") --> do - GP_Nsram.ext_fast_separation(Act_Nsram, 0.07.um) -end.().output("Gat.d", "Min. GatPoly space to Activ = 0.07") - -if $density - #-> do - # Gat_density.ext_with_density(0.0 .. 0.15, 'll') - #end.().output("GFil.g", "Min. global GatPoly density [%] = 15.00") -end - --> do - Cont_outside_EdgeSeal.ext_not(ContBar.ext_or(Cont_SQ)) -end.().output("Cnt.a", "Min. and max. Cont width = 0.16") --> do - Cont_outside_EdgeSeal.ext_fast_space(0.18.um, consider_intersecting_edges: false) -end.().output("Cnt.b", "Min. Cont space = 0.18") --> do - Metal1.ext_fast_width(0.16.um) -end.().output("M1.a", "Min. Metal1 width = 0.16") --> do - M1_Nsram.ext_fast_space(0.18.um, consider_intersecting_edges: false, ignore_non_axis_aligned_edges: true) -end.().output("M1.b", "Min. Metal1 space or notch = 0.18") - -if $density - #-> do - # M1_density.ext_with_density(0.0 .. 0.35, 'll') - #end.().output("M1.j", "Min. global Metal1 density [%] = 35.0") - -> do - M1_density.ext_with_density(0.6 .. 1.0, 'll') - end.().output("M1.k", "Max. global Metal1 density [%] = 60.0") -end - --> do - Metal2.ext_fast_width(0.2.um) -end.().output("M2.a", "Min. Metal2 width = 0.20") --> do - M2_Nsram.ext_fast_space(0.21.um, consider_intersecting_edges: false, ignore_non_axis_aligned_edges: true) -end.().output("M2.b", "Min. Metal2 space or notch = 0.21") - -if $density - #-> do - # M2_density.ext_with_density(0.0 .. 0.35, 'll') - #end.().output("M2.j", "Min. global Metal2 density [%] = 35.00") - -> do - M2_density.ext_with_density(0.6 .. 1.0, 'll') - end.().output("M2.k", "Max. global Metal2 density [%] = 60.00") -end - --> do - Metal3.ext_fast_width(0.2.um) -end.().output("M3.a", "Min. Metal3 width = 0.20") --> do - M3_Nsram.ext_fast_space(0.21.um, consider_intersecting_edges: false, ignore_non_axis_aligned_edges: true) -end.().output("M3.b", "Min. Metal3 space or notch = 0.21") - -if $density - #-> do - # M3_density.ext_with_density(0.0 .. 0.35, 'll') - #end.().output("M3.j", "Min. global Metal3 density [%] = 35.00") - -> do - M3_density.ext_with_density(0.6 .. 1.0, 'll') - end.().output("M3.k", "Max. global Metal3 density [%] = 60.00") -end - --> do - Metal4.ext_fast_width(0.2.um) -end.().output("M4.a", "Min. Metal4 width = 0.20") --> do - M4_Nsram.ext_fast_space(0.21.um, consider_intersecting_edges: false, ignore_non_axis_aligned_edges: true) -end.().output("M4.b", "Min. Metal4 space or notch = 0.21") - -if $density - #-> do - # M4_density.ext_with_density(0.0 .. 0.35, 'll') - #end.().output("M4.j", "Min. global Metal4 density [%] = 35.00") - -> do - M4_density.ext_with_density(0.6 .. 1.0, 'll') - end.().output("M4.k", "Max. global Metal4 density [%] = 60.00") -end - --> do - Metal5.ext_fast_width(0.2.um) -end.().output("M5.a", "Min. Metal5 width = 0.20") --> do - M5_Nsram.ext_fast_space(0.21.um, consider_intersecting_edges: false, ignore_non_axis_aligned_edges: true) -end.().output("M5.b", "Min. Metal5 space or notch = 0.21") - -if $density - #-> do - # M5_density.ext_with_density(0.0 .. 0.35, 'll') - #end.().output("M5.j", "Min. global Metal5 density [%] = 35.00") - -> do - M5_density.ext_with_density(0.6 .. 1.0, 'll') - end.().output("M5.k", "Max. global Metal5 density [%] = 60.00") - #-> do - # M1_density.ext_with_density(0.0 .. 0.25, 'll', tile_size(800.0.um), tile_step(400.0.um)) - #end.().output("M1Fil.h", "Min. Metal1 and Metal1:filler coverage ratio for any 800 x 800 µm² chip area [%] = 25.00") - -> do - M1_density.ext_with_density(0.75 .. 1.0, 'll', tile_size(800.0.um), tile_step(400.0.um)) - end.().output("M1Fil.k", "Max. Metal1 and Metal1:filler coverage ratio for any 800 x 800 µm² chip area [%] = 75.00") - #-> do - # M2_density.ext_with_density(0.0 .. 0.25, 'll', tile_size(800.0.um), tile_step(400.0.um)) - #end.().output("M2Fil.h", "Min. Metal2 and Metal2:filler coverage ratio for any 800 x 800 µm² chip area [%] = 25.00") - -> do - M2_density.ext_with_density(0.75 .. 1.0, 'll', tile_size(800.0.um), tile_step(400.0.um)) - end.().output("M2Fil.k", "Max. Metal2 and Metal2:filler coverage ratio for any 800 x 800 µm² chip area [%] = 75.00") - #-> do - # M3_density.ext_with_density(0.0 .. 0.25, 'll', tile_size(800.0.um), tile_step(400.0.um)) - #end.().output("M3Fil.h", "Min. Metal3 and Metal3:filler coverage ratio for any 800 x 800 µm² chip area [%] = 25.00") - -> do - M3_density.ext_with_density(0.75 .. 1.0, 'll', tile_size(800.0.um), tile_step(400.0.um)) - end.().output("M3Fil.k", "Max. Metal3 and Metal3:filler coverage ratio for any 800 x 800 µm² chip area [%] = 75.00") - #-> do - # M4_density.ext_with_density(0.0 .. 0.25, 'll', tile_size(800.0.um), tile_step(400.0.um)) - #end.().output("M4Fil.h", "Min. Metal4 and Metal4:filler coverage ratio for any 800 x 800 µm² chip area [%] = 25.00") - -> do - M4_density.ext_with_density(0.75 .. 1.0, 'll', tile_size(800.0.um), tile_step(400.0.um)) - end.().output("M4Fil.k", "Max. Metal4 and Metal4:filler coverage ratio for any 800 x 800 µm² chip area [%] = 75.00") - #-> do - # M5_density.ext_with_density(0.0 .. 0.25, 'll', tile_size(800.0.um), tile_step(400.0.um)) - #end.().output("M5Fil.h", "Min. Metal5 and Metal5:filler coverage ratio for any 800 x 800 µm² chip area [%] = 25.00") - -> do - M5_density.ext_with_density(0.75 .. 1.0, 'll', tile_size(800.0.um), tile_step(400.0.um)) - end.().output("M5Fil.k", "Max. Metal5 and Metal5:filler coverage ratio for any 800 x 800 µm² chip area [%] = 75.00") -end - --> do - Via1_edgC1_out.outside(transG2L).ext_rectangles(false, false, [["==", 0.19.um]], [["==", 0.19.um]], nil, inverted: true) -end.().output("V1.a", "Min. and max. Via1 width = 0.19") --> do - Via1_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false) -end.().output("V1.b", "Min. Via1 space = 0.22") --> do - Via2_edgC1_out.outside(transG2L).ext_rectangles(false, false, [["==", 0.19.um]], [["==", 0.19.um]], nil, inverted: true) -end.().output("V2.a", "Min. and max. Via2 width = 0.19") --> do - Via2_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false) -end.().output("V2.b", "Min. Via2 space = 0.22") --> do - Via3_edgC1_out.outside(transG2L).ext_rectangles(false, false, [["==", 0.19.um]], [["==", 0.19.um]], nil, inverted: true) -end.().output("V3.a", "Min. and max. Via3 width = 0.19") --> do - Via3_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false) -end.().output("V3.b", "Min. Via3 space = 0.22") --> do - Via4_edgC1_out.outside(transG2L).ext_rectangles(false, false, [["==", 0.19.um]], [["==", 0.19.um]], nil, inverted: true) -end.().output("V4.a", "Min. and max. Via4 width = 0.19") --> do - Via4_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false) -end.().output("V4.b", "Min. Via4 space = 0.22") --> do - TopVia1_edgC1_out.ext_or(Vmim).ext_rectangles(false, false, [["==", 0.42.um]], [["==", 0.42.um]], nil, inverted: true) -end.().output("TV1.a", "Min. and max. TopVia1 width = 0.42") --> do - TopVia1_or_Vmim.ext_fast_space(0.42.um) -end.().output("TV1.b", "Min. TopVia1 space = 0.42") --> do - TopMetal1.ext_fast_width(1.64.um) -end.().output("TM1.a", "Min. TopMetal1 width = 1.64") --> do - TopMetal1.ext_fast_space(1.64.um) -end.().output("TM1.b", "Min. TopMetal1 space or notch = 1.64") - -if $density - #-> do - # TM1_density.ext_with_density(0.0 .. 0.25, 'll') - #end.().output("TM1.c", "Min. global TopMetal1 density [%] = 25.00") - -> do - TM1_density.ext_with_density(0.7 .. 1.0, 'll') - end.().output("TM1.d", "Max. global TopMetal1 density [%] = 70.00") -end - --> do - TopVia2_edgC1_out.ext_rectangles(false, false, [["==", 0.9.um]], [["==", 0.9.um]], nil, inverted: true) -end.().output("TV2.a", "Min. and max. TopVia2 width = 0.90") --> do - TopVia2.ext_fast_space(1.06.um) -end.().output("TV2.b", "Min. TopVia2 space = 1.06") --> do - TopMetal2.ext_fast_width(2.0.um) -end.().output("TM2.a", "Min. TopMetal2 width = 2.00") --> do - TopMetal2.ext_fast_space(2.0.um) -end.().output("TM2.b", "Min. TopMetal2 space or notch = 2.00") - -if $density - #-> do - # TM2_density.ext_with_density(0.0 .. 0.25, 'll') - #end.().output("TM2.c", "Min. global TopMetal2 density [%] = 25.00") - -> do - TM2_density.ext_with_density(0.7 .. 1.0, 'll') - end.().output("TM2.d", "Max. global TopMetal2 density [%] = 70.00") -end - --> do - Passiv.ext_fast_width(2.1.um) -end.().output("Pas.a", "Min. Passiv width = 2.10") --> do - Passiv.ext_fast_space(3.5.um) -end.().output("Pas.b", "Min. Passiv space or notch = 3.50") - -if $sanityRules - -> do - Activ_pin.ext_not(Activ) - end.().output("Pin.a", "Min. Activ enclosure of Activ:pin = 0.00") - -> do - GatPoly_pin.ext_not(GatPoly) - end.().output("Pin.b", "Min. GatPoly enclosure of GatPoly:pin = 0.00") - -> do - Metal1_pin.ext_not(Metal1) - end.().output("Pin.e", "Min. Metal1 enclosure of Metal1:pin = 0.00") - -> do - Metal2_pin.ext_not(Metal2) - end.().output("Pin.f.M2", "Min. Metal2 enclosure of Metal2:pin = 0.00") - -> do - Metal3_pin.ext_not(Metal3) - end.().output("Pin.f.M3", "Min. Metal3 enclosure of Metal3:pin = 0.00") - -> do - Metal4_pin.ext_not(Metal4) - end.().output("Pin.f.M4", "Min. Metal4 enclosure of Metal4:pin = 0.00") - -> do - Metal5_pin.ext_not(Metal5) - end.().output("Pin.f.M5", "Min. Metal5 enclosure of Metal5:pin = 0.00") - -> do - TopMetal1_pin.ext_not(TopMetal1) - end.().output("Pin.g", "Min. TopMetal1 enclosure of TopMetal1:pin = 0.00") - -> do - TopMetal2_pin.ext_not(TopMetal2) - end.().output("Pin.h", "Min. TopMetal2 enclosure of TopMetal2:pin = 0.00") -end - --> do - LBE.ext_fast_width(100.0.um) -end.().output("LBE.a", "Min. LBE width = 100.00") --> do - LBE.drc((width(projection) > 1500.0.um).polygons) -end.().output("LBE.b", "Max. LBE width = 1500.00") --> do - LBE.ext_with_area([[">", 250000.0.um2]]) -end.().output("LBE.b1", "Max. LBE area (µm²) = 250000.00") --> do - LBE.ext_fast_space(100.0.um, consider_intersecting_edges: false, ignore_non_axis_aligned_edges: true) -end.().output("LBE.c", "Min. LBE space or notch = 100.00") --> (;lbe_in_seal) do - lbe_in_seal = LBE.inside(EdgeSeal.holes.merge) - lbe_in_seal.ext_fast_separation(EdgeSeal, 150.0.um, consider_intersecting_edges: false, ignore_non_axis_aligned_edges: true) -end.().output("LBE.d", "Min. LBE space to inner edge of EdgeSeal = 150.00") --> do - LBE.with_holes.dup -end.().output("LBE.h", "No LBE ring allowed") - -if $density - -> do - LBE.ext_with_density(0.2 .. 1.0, 'll') - end.().output("LBE.i", "Max. global LBE density [%] = 20.00") -end - - -if $sanityRules - -> do - BiWind.dup - end.().output("forbidden.BiWind", "Forbidden drawn layer BiWind on GDS layer 3/0 = 3/0") - -> do - PEmWind.dup - end.().output("forbidden.PEmWind", "Forbidden drawn layer PEmWind on GDS layer 11/0 = 11/0") - -> do - BasPoly.dup - end.().output("forbidden.BasPoly", "Forbidden drawn layer BasPoly on GDS layer 13/0 = 13/0") - -> do - DeepCo.dup - end.().output("forbidden.DeepCo", "Forbidden drawn layer DeepCo on GDS layer 35/0 = 35/0") - -> do - PEmPoly.dup - end.().output("forbidden.PEmPoly", "Forbidden drawn layer PEmPoly on GDS layer 53/0 = 53/0") - -> do - EmPoly.dup - end.().output("forbidden.EmPoly", "Forbidden gen./drawn layer EmPoly on GDS layer 53/0 = 53/0") - -> do - LDMOS.dup - end.().output("forbidden.LDMOS", "Forbidden drawn layer LDMOS on GDS layer 57/0 = 57/0") - -> do - PBiWind.dup - end.().output("forbidden.PBiWind", "Forbidden drawn layer PBiWind on GDS layer 58/0 = 58/0") - -> do - Flash.dup - end.().output("forbidden.Flash", "Forbidden drawn layer Flash on GDS layer 71/0 = 71/0") - -> do - ColWind.dup - end.().output("forbidden.ColWind", "Forbidden drawn layer ColWind on GDS layer 139/0 = 139/0") -end - -puts("Number of DRC errors: #{$drc_error_count}") - - \ No newline at end of file