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}")