Files
tt-support-tools/precheck/tech-files/sg13g2_mr.lydrc
T
Uri Shaked b57f798ec4 fix(precheck): disable additional min density checks for ihp-sg13g2
Disable GatPoly, Activ, and TopMetal2 min density checks
2025-12-05 10:00:44 +02:00

1052 lines
42 KiB
XML

<?xml version='1.0' encoding='utf-8'?>
<!--
Copyright 2024 IHP PDK Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<klayout-macro>
<description />
<version />
<category>drc</category>
<prolog />
<epilog />
<doc />
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut />
<show-in-menu>true</show-in-menu>
<group-name>drc_scripts</group-name>
<menu-path>tools_menu.drc.end</menu-path>
<interpreter>dsl</interpreter>
<dsl-interpreter-name>drc-dsl-xml</dsl-interpreter-name>
<text># Supported variables that can be set using "-rd &lt;name&gt;=&lt;value&gt;" 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 &amp; 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 == "&gt;"
lower_bound = value + 1e-6
elsif relation == "&lt;"
upper_bound = value
elsif relation == "=="
output_layer = self.with_area(value)
elsif relation == "!="
output_layer = self.without_area(value)
elsif relation == "&gt;="
lower_bound = value
elsif relation == "&lt;="
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] == "&gt;"
output_bool = output_bool &amp;&amp; (value &gt; expression[1])
elsif expression[0] == "&lt;"
output_bool = output_bool &amp;&amp; (value &lt; expression[1])
elsif expression[0] == "=="
output_bool = output_bool &amp;&amp; (value == expression[1])
elsif expression[0] == "!="
output_bool = output_bool &amp;&amp; (value != expression[1])
elsif expression[0] == "&gt;="
output_bool = output_bool &amp;&amp; (value &gt;= expression[1])
elsif expression[0] == "&lt;="
output_bool = output_bool &amp;&amp; (value &lt;= 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 &amp;&amp; ( !constraint2 || constraint1.length() &gt; 1 || constraint1[0][0] != "==") ) ||
( constraint2 &amp;&amp; ( constraint2.length() &gt; 1 || constraint2[0][0] != "==" ) ) ||
( constraint1 &amp;&amp; constraint2 &amp;&amp; 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([["&gt;", (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)
-&gt; do
Activ_Act_a.dup
end.().output("Act.a", "Min. Activ width = 0.15")
-&gt; do
Act_Nsram.ext_fast_space(0.21.um)
end.().output("Act.b", "Min. Activ space or notch = 0.21")
if $density
#-&gt; do
# Act_density.ext_with_density(0.0 .. 0.35, 'll')
#end.().output("AFil.g", "Min. global Activ density [%] = 35.00")
-&gt; do
Act_density.ext_with_density(0.55 .. 1.0, 'll')
end.().output("AFil.g1", "Max. global Activ density [%] = 55.00")
#-&gt; 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")
-&gt; 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
-&gt; do
ThickGateOx_TGO_f.dup
end.().output("TGO.f", "Min. ThickGateOx width = 0.86")
-&gt; do
GP_Nsram_Gat_a.dup
end.().output("Gat.a", "Min. GatPoly width = 0.13")
-&gt; do
GP_Nsram_Gat_b.dup
end.().output("Gat.b", "Min. GatPoly space or notch = 0.18")
-&gt; do
GP_Nsram.ext_fast_separation(Act_Nsram, 0.07.um)
end.().output("Gat.d", "Min. GatPoly space to Activ = 0.07")
if $density
#-&gt; do
# Gat_density.ext_with_density(0.0 .. 0.15, 'll')
#end.().output("GFil.g", "Min. global GatPoly density [%] = 15.00")
end
-&gt; do
Cont_outside_EdgeSeal.ext_not(ContBar.ext_or(Cont_SQ))
end.().output("Cnt.a", "Min. and max. Cont width = 0.16")
-&gt; do
Cont_outside_EdgeSeal.ext_fast_space(0.18.um, consider_intersecting_edges: false)
end.().output("Cnt.b", "Min. Cont space = 0.18")
-&gt; do
Metal1.ext_fast_width(0.16.um)
end.().output("M1.a", "Min. Metal1 width = 0.16")
-&gt; 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
#-&gt; do
# M1_density.ext_with_density(0.0 .. 0.35, 'll')
#end.().output("M1.j", "Min. global Metal1 density [%] = 35.0")
-&gt; do
M1_density.ext_with_density(0.6 .. 1.0, 'll')
end.().output("M1.k", "Max. global Metal1 density [%] = 60.0")
end
-&gt; do
Metal2.ext_fast_width(0.2.um)
end.().output("M2.a", "Min. Metal2 width = 0.20")
-&gt; 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
#-&gt; do
# M2_density.ext_with_density(0.0 .. 0.35, 'll')
#end.().output("M2.j", "Min. global Metal2 density [%] = 35.00")
-&gt; do
M2_density.ext_with_density(0.6 .. 1.0, 'll')
end.().output("M2.k", "Max. global Metal2 density [%] = 60.00")
end
-&gt; do
Metal3.ext_fast_width(0.2.um)
end.().output("M3.a", "Min. Metal3 width = 0.20")
-&gt; 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
#-&gt; do
# M3_density.ext_with_density(0.0 .. 0.35, 'll')
#end.().output("M3.j", "Min. global Metal3 density [%] = 35.00")
-&gt; do
M3_density.ext_with_density(0.6 .. 1.0, 'll')
end.().output("M3.k", "Max. global Metal3 density [%] = 60.00")
end
-&gt; do
Metal4.ext_fast_width(0.2.um)
end.().output("M4.a", "Min. Metal4 width = 0.20")
-&gt; 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
#-&gt; do
# M4_density.ext_with_density(0.0 .. 0.35, 'll')
#end.().output("M4.j", "Min. global Metal4 density [%] = 35.00")
-&gt; do
M4_density.ext_with_density(0.6 .. 1.0, 'll')
end.().output("M4.k", "Max. global Metal4 density [%] = 60.00")
end
-&gt; do
Metal5.ext_fast_width(0.2.um)
end.().output("M5.a", "Min. Metal5 width = 0.20")
-&gt; 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
#-&gt; do
# M5_density.ext_with_density(0.0 .. 0.35, 'll')
#end.().output("M5.j", "Min. global Metal5 density [%] = 35.00")
-&gt; do
M5_density.ext_with_density(0.6 .. 1.0, 'll')
end.().output("M5.k", "Max. global Metal5 density [%] = 60.00")
#-&gt; 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")
-&gt; 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")
#-&gt; 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")
-&gt; 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")
#-&gt; 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")
-&gt; 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")
#-&gt; 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")
-&gt; 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")
#-&gt; 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")
-&gt; 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
-&gt; 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")
-&gt; do
Via1_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false)
end.().output("V1.b", "Min. Via1 space = 0.22")
-&gt; 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")
-&gt; do
Via2_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false)
end.().output("V2.b", "Min. Via2 space = 0.22")
-&gt; 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")
-&gt; do
Via3_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false)
end.().output("V3.b", "Min. Via3 space = 0.22")
-&gt; 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")
-&gt; do
Via4_edgC1_out.ext_fast_space(0.22.um, consider_intersecting_edges: false)
end.().output("V4.b", "Min. Via4 space = 0.22")
-&gt; 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")
-&gt; do
TopVia1_or_Vmim.ext_fast_space(0.42.um)
end.().output("TV1.b", "Min. TopVia1 space = 0.42")
-&gt; do
TopMetal1.ext_fast_width(1.64.um)
end.().output("TM1.a", "Min. TopMetal1 width = 1.64")
-&gt; do
TopMetal1.ext_fast_space(1.64.um)
end.().output("TM1.b", "Min. TopMetal1 space or notch = 1.64")
if $density
#-&gt; do
# TM1_density.ext_with_density(0.0 .. 0.25, 'll')
#end.().output("TM1.c", "Min. global TopMetal1 density [%] = 25.00")
-&gt; do
TM1_density.ext_with_density(0.7 .. 1.0, 'll')
end.().output("TM1.d", "Max. global TopMetal1 density [%] = 70.00")
end
-&gt; 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")
-&gt; do
TopVia2.ext_fast_space(1.06.um)
end.().output("TV2.b", "Min. TopVia2 space = 1.06")
-&gt; do
TopMetal2.ext_fast_width(2.0.um)
end.().output("TM2.a", "Min. TopMetal2 width = 2.00")
-&gt; do
TopMetal2.ext_fast_space(2.0.um)
end.().output("TM2.b", "Min. TopMetal2 space or notch = 2.00")
if $density
#-&gt; do
# TM2_density.ext_with_density(0.0 .. 0.25, 'll')
#end.().output("TM2.c", "Min. global TopMetal2 density [%] = 25.00")
-&gt; do
TM2_density.ext_with_density(0.7 .. 1.0, 'll')
end.().output("TM2.d", "Max. global TopMetal2 density [%] = 70.00")
end
-&gt; do
Passiv.ext_fast_width(2.1.um)
end.().output("Pas.a", "Min. Passiv width = 2.10")
-&gt; do
Passiv.ext_fast_space(3.5.um)
end.().output("Pas.b", "Min. Passiv space or notch = 3.50")
if $sanityRules
-&gt; do
Activ_pin.ext_not(Activ)
end.().output("Pin.a", "Min. Activ enclosure of Activ:pin = 0.00")
-&gt; do
GatPoly_pin.ext_not(GatPoly)
end.().output("Pin.b", "Min. GatPoly enclosure of GatPoly:pin = 0.00")
-&gt; do
Metal1_pin.ext_not(Metal1)
end.().output("Pin.e", "Min. Metal1 enclosure of Metal1:pin = 0.00")
-&gt; do
Metal2_pin.ext_not(Metal2)
end.().output("Pin.f.M2", "Min. Metal2 enclosure of Metal2:pin = 0.00")
-&gt; do
Metal3_pin.ext_not(Metal3)
end.().output("Pin.f.M3", "Min. Metal3 enclosure of Metal3:pin = 0.00")
-&gt; do
Metal4_pin.ext_not(Metal4)
end.().output("Pin.f.M4", "Min. Metal4 enclosure of Metal4:pin = 0.00")
-&gt; do
Metal5_pin.ext_not(Metal5)
end.().output("Pin.f.M5", "Min. Metal5 enclosure of Metal5:pin = 0.00")
-&gt; do
TopMetal1_pin.ext_not(TopMetal1)
end.().output("Pin.g", "Min. TopMetal1 enclosure of TopMetal1:pin = 0.00")
-&gt; do
TopMetal2_pin.ext_not(TopMetal2)
end.().output("Pin.h", "Min. TopMetal2 enclosure of TopMetal2:pin = 0.00")
end
-&gt; do
LBE.ext_fast_width(100.0.um)
end.().output("LBE.a", "Min. LBE width = 100.00")
-&gt; do
LBE.drc((width(projection) &gt; 1500.0.um).polygons)
end.().output("LBE.b", "Max. LBE width = 1500.00")
-&gt; do
LBE.ext_with_area([["&gt;", 250000.0.um2]])
end.().output("LBE.b1", "Max. LBE area (µm²) = 250000.00")
-&gt; 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")
-&gt; (;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")
-&gt; do
LBE.with_holes.dup
end.().output("LBE.h", "No LBE ring allowed")
if $density
-&gt; do
LBE.ext_with_density(0.2 .. 1.0, 'll')
end.().output("LBE.i", "Max. global LBE density [%] = 20.00")
end
if $sanityRules
-&gt; do
BiWind.dup
end.().output("forbidden.BiWind", "Forbidden drawn layer BiWind on GDS layer 3/0 = 3/0")
-&gt; do
PEmWind.dup
end.().output("forbidden.PEmWind", "Forbidden drawn layer PEmWind on GDS layer 11/0 = 11/0")
-&gt; do
BasPoly.dup
end.().output("forbidden.BasPoly", "Forbidden drawn layer BasPoly on GDS layer 13/0 = 13/0")
-&gt; do
DeepCo.dup
end.().output("forbidden.DeepCo", "Forbidden drawn layer DeepCo on GDS layer 35/0 = 35/0")
-&gt; do
PEmPoly.dup
end.().output("forbidden.PEmPoly", "Forbidden drawn layer PEmPoly on GDS layer 53/0 = 53/0")
-&gt; do
EmPoly.dup
end.().output("forbidden.EmPoly", "Forbidden gen./drawn layer EmPoly on GDS layer 53/0 = 53/0")
-&gt; do
LDMOS.dup
end.().output("forbidden.LDMOS", "Forbidden drawn layer LDMOS on GDS layer 57/0 = 57/0")
-&gt; do
PBiWind.dup
end.().output("forbidden.PBiWind", "Forbidden drawn layer PBiWind on GDS layer 58/0 = 58/0")
-&gt; do
Flash.dup
end.().output("forbidden.Flash", "Forbidden drawn layer Flash on GDS layer 71/0 = 71/0")
-&gt; 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}")
</text>
</klayout-macro>