WIP: refactoring of DRC Ruby code

This commit is contained in:
Matthias Koefferlein 2021-01-03 19:03:30 +01:00
parent 43302dbe68
commit f3d8fb4a43
10 changed files with 2805 additions and 954 deletions

View File

@ -397,6 +397,12 @@ CompoundRegionLogicalBoolOperationNode::CompoundRegionLogicalBoolOperationNode (
// .. nothing yet .. // .. nothing yet ..
} }
CompoundRegionLogicalBoolOperationNode::ResultType
CompoundRegionLogicalBoolOperationNode::result_type () const
{
return Region;
}
std::string CompoundRegionLogicalBoolOperationNode::generated_description () const std::string CompoundRegionLogicalBoolOperationNode::generated_description () const
{ {
std::string r; std::string r;

View File

@ -416,7 +416,7 @@ public:
virtual std::string generated_description () const; virtual std::string generated_description () const;
// specifies the result type // specifies the result type
virtual ResultType result_type () const { return Region; } virtual ResultType result_type () const;
// the different computation slots // the different computation slots
virtual void do_compute_local (db::Layout *layout, const shape_interactions<db::Polygon, db::Polygon> &interactions, std::vector<std::unordered_set<db::Polygon> > &results, size_t max_vertex_count, double area_ratio) const virtual void do_compute_local (db::Layout *layout, const shape_interactions<db::Polygon, db::Polygon> &interactions, std::vector<std::unordered_set<db::Polygon> > &results, size_t max_vertex_count, double area_ratio) const

View File

@ -0,0 +1,947 @@
# $autorun-early
module DRC
class DRCOpNode
attr_accessor :description
attr_accessor :engine
def initialize(engine, node = nil)
@node = node
self.engine = engine
self.description = "Basic"
end
def create_node(cache)
n = cache[self.object_id]
if !n
n = self.do_create_node(cache)
cache[self.object_id] = n
end
n
end
def do_create_node(cache)
@node
end
def dump(indent)
return indent + self.description
end
def _build_geo_bool_node(other, op)
if ! other.is_a?(DRCOpNode)
raise("Second argument to #{op.to_s} must be a DRC expression")
end
DRCOpNodeBool::new(@engine, op, self, other)
end
%w(& - ^ | +).each do |f|
eval <<"CODE"
def #{f}(other)
self.engine._context("#{f}") do
self._build_geo_bool_node(other, :#{f})
end
end
CODE
end
def !()
self.engine._context("!") do
if self.respond_to?(:inverted)
return self.inverted
else
empty = RBA::CompoundRegionOperationNode::new_empty(RBA::CompoundRegionOperationNode::ResultType::Region)
DRCOpNodeCase::new(@engine, [ self, DRCOpNode::new(@engine, empty), @engine.primary ])
end
end
end
def _check_numeric(v, symbol)
if ! v.is_a?(Float) && ! v.is_a?(1.class)
if symbol
raise("Argument '#{symbol}' (#{v.inspect}) isn't numeric in operation '#{self.description}'")
else
raise("Argument (#{v.inspect}) isn't numeric in operation '#{self.description}'")
end
end
end
def _make_value(v, symbol)
self._check_numeric(v, symbol)
@engine._prep_value(v)
end
def _make_area_value(v, symbol)
self._check_numeric(v, symbol)
@engine._prep_area_value(v)
end
def area
DRCOpNodeAreaFilter::new(@engine, self)
end
def perimeter
DRCOpNodePerimeterFilter::new(@engine, self)
end
def bbox_min
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMinDim, self)
end
def bbox_max
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxMaxDim, self)
end
def bbox_width
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxWidth, self)
end
def bbox_height
DRCOpNodeBBoxParameterFilter::new(@engine, RBA::CompoundRegionOperationNode::BoxHeight, self)
end
def length
DRCOpNodeEdgeLengthFilter::new(@engine, self)
end
def angle
DRCOpNodeEdgeOrientationFilter::new(@engine, self)
end
def rounded_corners(inner, outer, n)
self.engine._context("rounded_corners") do
self._check_numeric(n, :n)
DRCOpNodeFilter::new(@engine, self, :new_rounded_corners, "rounded_corners", self.make_value(inner, :inner), self.make_value(outer, :outer), n)
end
end
def smoothed(d)
self.engine._context("smoothed") do
DRCOpNodeFilter::new(@engine, self, :new_smoothed, "smoothed", self.make_value(d, :d))
end
end
def corners(as_dots = DRCAsDots::new(false))
self.engine._context("corners") do
if as_dots.is_a?(DRCAsDots)
as_dots = as_dots.value
else
raise("Invalid argument (#{as_dots.inspect}) for 'corners' method")
end
DRCOpNodeCornersFilter::new(@engine, self, as_dots)
end
end
%w(middle extent_refs).each do |f|
eval <<"CODE"
def #{f}(*args)
self.engine._context("#{f}") do
f = []
as_edges = false
@@std_refs ||= {
:center => [0.5] * 4,
:c => [0.5] * 4,
:bottom_center => [ 0.5, 0.0, 0.5, 0.0 ],
:bc => [ 0.5, 0.0, 0.5, 0.0 ],
:bottom_left => [ 0.0, 0.0, 0.0, 0.0 ],
:bl => [ 0.0, 0.0, 0.0, 0.0 ],
:bottom_right => [ 1.0, 0.0, 1.0, 0.0 ],
:br => [ 1.0, 0.0, 1.0, 0.0 ],
:top_center => [ 0.5, 1.0, 0.5, 1.0 ],
:tc => [ 0.5, 1.0, 0.5, 1.0 ],
:top_left => [ 0.0, 1.0, 0.0, 1.0 ],
:tl => [ 0.0, 1.0, 0.0, 1.0 ],
:top_right => [ 1.0, 1.0, 1.0, 1.0 ],
:tr => [ 1.0, 1.0, 1.0, 1.0 ],
:left_center => [ 0.0, 0.5, 0.0, 0.5 ],
:lc => [ 0.0, 0.5, 0.0, 0.5 ],
:right_center => [ 1.0, 0.5, 1.0, 0.5 ],
:rc => [ 1.0, 0.5, 1.0, 0.5 ],
:south => [ 0.5, 0.0, 0.5, 0.0 ],
:s => [ 0.5, 0.0, 0.5, 0.0 ],
:left => [ 0.0, 0.0, 0.0, 1.0 ],
:l => [ 0.0, 0.0, 0.0, 1.0 ],
:bottom => [ 1.0, 0.0, 0.0, 0.0 ],
:b => [ 1.0, 0.0, 0.0, 0.0 ],
:right => [ 1.0, 1.0, 1.0, 0.0 ],
:r => [ 1.0, 1.0, 1.0, 0.0 ],
:top => [ 0.0, 1.0, 1.0, 1.0 ],
:t => [ 0.0, 1.0, 1.0, 1.0 ]
}
args.each_with_index do |a,ia|
if a.is_a?(1.0.class) && :#{f} != :middle
f << a
elsif a.is_a?(DRCAsDots)
as_edges = a.value
elsif @@std_refs[a] && :#{f} != :middle
f = @@std_refs[a]
else
raise("Invalid argument #" + (ia + 1).to_s + " (" + a.inspect + ") for '#{f}' method on operation '" + self.description + "' - needs to be numeric or 'as_dots/as_edges'")
end
end
if f.size == 2
f = f + f
else
f = (f + [0.5] * 4)[0..3]
end
if as_edges
return DRCOpNodeRelativeExtents::new(self, true, *f)
else
# add oversize for point- and edge-like regions
zero_area = (f[0] - f[2]).abs < 1e-7 || (f[1] - f[3]).abs < 1e-7
f += [ zero_area ? 1 : 0 ] * 2
return DRCOpNodeRelativeExtents::new(self, false, *f)
end
end
end
CODE
end
def odd_polygons
return DRCOpNodeFilter::new(@engine, self, :new_strange_polygons_filter, "odd_polygon")
end
def rectangles
return DRCOpNodeFilter::new(@engine, self, :new_rectangle_filter, "rectangle")
end
def rectilinear
return DRCOpNodeFilter::new(@engine, self, :new_rectilinear_filter, "rectilinear")
end
def holes
return DRCOpNodeFilter::new(@engine, self, :new_holes, "holes")
end
def hulls
return DRCOpNodeFilter::new(@engine, self, :new_hulls, "hull")
end
def edges
return DRCOpNodeFilter::new(@engine, self, :new_edges, "edges")
end
def sized(*args)
self.engine._context("sized") do
dist = 0
mode = 2
values = []
args.each_with_index do |a,ia|
if a.is_a?(1.class) || a.is_a?(Float)
v = self._make_value(a, "argument ##{ia + 1}")
v.abs > dist && dist = v.abs
values.push(v)
elsif a.is_a?(DRCSizingMode)
mode = a.value
end
end
args = []
if values.size < 1
raise("sized: Method requires one or two sizing values")
elsif values.size > 2
raise("sized: Method must not have more than two values")
else
args << values[0]
args << values[-1]
end
args << mode
DRCOpNodeFilter::new(@engine, self, :new_sized, "sized", *args)
end
end
def extents(e = 0)
self.engine._context("extents") do
DRCOpNodeFilter::new(@engine, self, :new_extents, "extents", self._make_value(e, :e))
end
end
def first_edges
DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_first_edges, "first_edges")
end
def second_edges
DRCOpNodeFilter::new(@engine, self, :new_edge_pair_to_second_edges, "second_edges")
end
def end_segments(length, fraction = 0.0)
self.engine._context("end_segments") do
self._check_numeric(fraction, :fraction)
DRCOpNodeFilter::new(@engine, self, :new_end_segments, "end_segments", self._make_value(length, :length), fraction)
end
end
def start_segments(length, fraction = 0.0)
self.engine._context("start_segments") do
self._check_numeric(fraction, :fraction)
DRCOpNodeFilter::new(@engine, self, :new_start_segments, "start_segments", self._make_value(length, :length), fraction)
end
end
def centers(length, fraction = 0.0)
self.engine._context("centers") do
self._check_numeric(fraction, :fraction)
DRCOpNodeFilter::new(@engine, self, :new_centers, "centers", self._make_value(length, :length), fraction)
end
end
def extended(*args)
self.engine._context("extended") do
av = [ 0, 0, 0, 0 ]
args.each_with_index do |a,i|
if a.is_a?(Hash)
a[:begin] && av[0] = self._make_value(a[:begin], :begin)
a[:end] && av[1] = self._make_value(a[:end], :end)
a[:out] && av[2] = self._make_value(a[:out], :out)
a[:in] && av[3] = self._make_value(a[:in], :in)
a[:joined] && av[4] = true
elsif i < 4
av[i] = self._make_value(a, "argument " + (i+1).to_s)
else
raise("Too many arguments for method '#{f}' (1 to 5 expected)")
end
end
DRCOpNodeFilter::new(@engine, self, :new_extended, "extended", *args)
end
end
def extended_in(e)
self.engine._context("extended_in") do
DRCOpNodeFilter::new(@engine, self, :new_extended_in, "extended_in", self._make_value(e))
end
end
def extended_out(e)
self.engine._context("extended_out") do
DRCOpNodeFilter::new(@engine, self, :new_extended_out, "extended_out", self._make_value(e))
end
end
def polygons
self.engine._context("polygons") do
DRCOpNodeFilter::new(@engine, self, :new_polygons, "polygons")
end
end
end
class DRCOpNodeLogicalBool < DRCOpNode
attr_accessor :children
attr_accessor :op
def initialize(engine, op)
super(engine)
self.children = []
self.op = op
self.description = op.to_s
end
def dump(indent)
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
end
def do_create_node(cache)
log_op = {
:if_all => RBA::CompoundRegionOperationNode::LogicalOp::LogAnd,
:if_any => RBA::CompoundRegionOperationNode::LogicalOp::LogOr,
:if_none => RBA::CompoundRegionOperationNode::LogicalOp::LogOr
} [self.op]
invert = {
:if_all => false,
:if_any => false,
:if_none => true
} [self.op]
RBA::CompoundRegionOperationNode::new_logical_boolean(log_op, invert, self.children.collect { |c| c.create_node(cache) })
end
end
class DRCOpNodeBool < DRCOpNode
attr_accessor :children
attr_accessor :op
def initialize(engine, op, a, b)
super(engine)
self.children = [a, b]
self.op = op
self.description = "Geometrical #{op.to_s}"
end
def dump(indent)
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
end
def do_create_node(cache)
bool_op = { :& => RBA::CompoundRegionOperationNode::GeometricalOp::And,
:+ => RBA::CompoundRegionOperationNode::GeometricalOp::Or,
:| => RBA::CompoundRegionOperationNode::GeometricalOp::Or,
:- => RBA::CompoundRegionOperationNode::GeometricalOp::Not,
:^ => RBA::CompoundRegionOperationNode::GeometricalOp::Xor }[self.op]
nodes = self.children.collect do |c|
n = c.create_node(cache)
if n.result_type == RBA::CompoundRegionOperationNode::ResultType::EdgePairs
n = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(n)
end
n
end
RBA::CompoundRegionOperationNode::new_geometrical_boolean(bool_op, *nodes)
end
end
class DRCOpNodeCase < DRCOpNode
attr_accessor :children
def initialize(engine, children)
super(engine)
self.children = children
self.description = "switch"
end
def dump(indent)
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
end
def do_create_node(cache)
RBA::CompoundRegionOperationNode::new_case(self.children.collect { |c| c.create_node(cache) })
end
end
class DRCOpNodeWithCompare < DRCOpNode
attr_accessor :reverse
attr_accessor :original
attr_accessor :lt, :le, :gt, :ge, :arg
def initialize(engine, original = nil, reverse = false)
super(engine)
self.reverse = reverse
self.original = original
self.description = original ? original.description : "BasicWithCompare"
end
def _description_for_dump
self.description
end
def dump(indent = "")
if self.original
return "@temp (should not happen)"
else
cmp = []
self.lt && (cmp << ("<%.12g" % self.lt))
self.le && (cmp << ("<=%.12g" % self.le))
self.gt && (cmp << (">%.12g" % self.gt))
self.ge && (cmp << (">=%.12g" % self.ge))
return indent + self.description + " " + cmp.join(" ")
end
end
def _check_bounds
if (self.lt || self.le) && (self.gt || self.ge)
epsilon = 1e-10
lower = self.ge ? self.ge - epsilon : self.gt + epsilon
upper = self.le ? self.le + epsilon : self.lt - epsilon
if lower > upper - epsilon
raise("Lower bound is larger than upper bound")
end
end
end
def set_lt(value)
(self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
self.lt = value
self._check_bounds
end
def set_le(value)
(self.lt || self.le) && raise("'" + self.description + "' already has an upper bound of " + ("%.12g" % (self.lt || self.le)))
self.le = value
self._check_bounds
end
def set_gt(value)
(self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
self.gt = value
self._check_bounds
end
def set_ge(value)
(self.gt || self.ge) && raise("'" + self.description + "' already has an lower bound of " + ("%.12g" % (self.gt || self.ge)))
self.ge = value
self._check_bounds
end
def coerce(something)
[ DRCOpNodeWithCompare::new(self.engine, self, true), something ]
end
def _self_or_original
return self.original || self
end
def ==(other)
if !(other.is_a?(Float) || other.is_a?(Integer))
raise("== operator needs a numerical argument for '" + self.description + "' argument")
end
res = self._self_or_original
res.set_le(other)
res.set_ge(other)
return res
end
def <(other)
if !(other.is_a?(Float) || other.is_a?(Integer))
raise("< operator needs a numerical argument for '" + self.description + "' argument")
end
res = self._self_or_original
if reverse
res.set_gt(other)
else
res.set_lt(other)
end
return res
end
def <=(other)
if !(other.is_a?(Float) || other.is_a?(Integer))
raise("<= operator needs a numerical argument for '" + self.description + "' argument")
end
res = self._self_or_original
if reverse
res.set_ge(other)
else
res.set_le(other)
end
return res
end
def >(other)
if !(other.is_a?(Float) || other.is_a?(Integer))
raise("> operator needs a numerical argument for '" + self.description + "' argument")
end
res = self._self_or_original
if reverse
res.set_lt(other)
else
res.set_gt(other)
end
return res
end
def >=(other)
if !(other.is_a?(Float) || other.is_a?(Integer))
raise(">= operator needs a numerical argument for '" + self.description + "' argument")
end
res = self._self_or_original
if reverse
res.set_le(other)
else
res.set_ge(other)
end
return res
end
end
class DRCOpNodeAreaFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverted
def initialize(engine, input)
super(engine)
self.input = input
self.inverted = false
self.description = "area"
end
def _description_for_dump
self.inverted ? "area" : "not_area"
end
def do_create_node(cache)
args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? make_area_value(self.gt) + 1 : (self.ge ? make_area_value(self.ge) : 0))
if self.lt || self.le
args << self.lt ? make_area_value(self.lt) : make_area_value(self.le) - 1
end
RBA::CompoundRegionOperationNode::new_area_filter(*args)
end
def inverted
res = self.dup
res.inverted = !res.inverted
return res
end
end
class DRCOpNodeEdgeLengthFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverted
def initialize(engine, input)
super(engine)
self.input = input
self.inverted = false
self.description = "length"
end
def _description_for_dump
self.inverted ? "length" : "not_length"
end
def do_create_node(cache)
args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
if self.lt || self.le
args << self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
end
RBA::CompoundRegionOperationNode::new_edge_length_filter(*args)
end
def inverted
res = self.dup
res.inverted = !res.inverted
return res
end
end
class DRCOpNodeEdgeOrientationFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverted
def initialize(engine, input)
super(engine)
self.input = input
self.inverted = false
self.description = "angle"
end
def _description_for_dump
self.inverted ? "angle" : "not_angle"
end
def do_create_node(cache)
args = [ self.input.create_node(cache), self.inverse ]
angle_delta = 1e-6
args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0))
args << (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0))
RBA::CompoundRegionOperationNode::new_edge_orientation_filter(*args)
end
def inverted
res = self.dup
res.inverted = !res.inverted
return res
end
end
class DRCOpNodePerimeterFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :inverted
def initialize(engine, input)
super(engine)
self.input = input
self.inverted = false
self.description = "perimeter"
end
def _description_for_dump
self.inverted ? "perimeter" : "not_perimeter"
end
def do_create_node(cache)
args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
if self.lt || self.le
args << self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
end
RBA::CompoundRegionOperationNode::new_perimeter_filter(*args)
end
def inverted
res = self.dup
res.inverted = !res.inverted
return res
end
end
class DRCOpNodeInteractingWithCount < DRCOpNodeWithCompare
attr_accessor :a, :b
attr_accessor :inverted
attr_accessor :op
def initialize(engine, a, b, op)
super(engine)
self.a = a
self.b = b
self.op = op
self.inverted = false
self.description = (self.inverted ? "" : "not_") + self.op.to_s
end
def do_create_node(cache)
args = [ self.a.create_node(cache), self.b.create_node(cache), self.inverse ]
args << (self.gt ? self.gt + 1 : (self.ge ? self.ge : 0))
if self.lt || self.le
args << self.lt ? self.lt : self.le - 1
end
factory = { :covering => :new_enclosing,
:overlapping => :new_overlapping,
:interacting => :new_interacting }[self.op]
RBA::CompoundRegionOperationNode::send(factory, *args)
end
def inverted
res = self.dup
res.inverted = !res.inverted
return res
end
end
class DRCOpNodeInteracting < DRCOpNode
attr_accessor :a, :b
attr_accessor :inverted
attr_accessor :op
def initialize(engine, a, b, op)
super(engine)
self.a = a
self.b = b
self.op = op
self.inverted = false
self.description = (self.inverted ? "" : "not_") + self.op.to_s
end
def do_create_node(cache)
factory = { :inside => :new_inside,
:outside => :new_outside }[self.op]
RBA::CompoundRegionOperationNode::send(factory, self.a.create_node(cache), self.b.create_node(cache), self.inverse)
end
def inverted
res = self.dup
res.inverted = !res.inverted
return res
end
end
class DRCOpNodeFilter < DRCOpNode
attr_accessor :input
attr_accessor :factory
attr_accessor :args
def initialize(engine, input, factory, description, *args)
super(engine)
self.input = input
self.factory = factory
self.args = args
self.description = description
end
def dump(indent)
if self.args.size > 0
return self.description + "(" + self.args.collect { |a| a.inspect }.join(",") + ")\n" + input.dump(" " + indent)
else
return self.description + "\n" + input.dump(" " + indent)
end
end
def do_create_node(cache)
RBA::CompoundRegionOperationNode::send(self.factory, self.input.create_node(cache), *args)
end
end
class DRCOpNodeCheck < DRCOpNodeWithCompare
attr_accessor :other
attr_accessor :check
attr_accessor :args
def initialize(engine, check, other, *args)
super(engine)
self.check = check
self.other = other
self.args = args
self.description = check.to_s
end
def _description_for_dump
if self.args.size > 0
return self.description + "(" + self.args.collect { |a| a.inspect }.join(",") + ")"
else
return self.description
end
end
def do_create_node(cache)
if !(self.lt || self.le) && !(self.gt || self.ge)
raise("No value given for check #{self.check}")
end
factory = { :width => :new_width_check, :space => :new_space_check,
:notch => :new_notch_check, :separation => :new_separation_check,
:isolated => :new_isolated_check, :overlap => :new_overlap_check,
:enclosing => :new_inside_check }[self.check]
if self.lt || self.le
dmin = self.le ? self._make_value(self.le, :le) + 1 : self._make_value(self.lt, :lt)
res = RBA::CompoundRegionOperationNode::send(factory, dmin, *self.args)
else
res = nil
end
if self.gt || self.ge
dmax = self.ge ? self._make_value(self.ge, :ge) : self._make_value(self.gt, :gt) + 1
max_check = RBA::CompoundRegionOperationNode::send(factory, dmax, *self.args + [ true ])
res_max = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(max_check)
if res
if self.check == :width || self.check == :notch
# Same polygon check - we need to take both edges of the result
and_with = RBA::CompoundRegionOperationNode::new_edges(res)
else
and_with = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(res)
end
res = RBA::CompoundRegionOperationNode::new_geometrical_boolean(RBA::CompoundRegionOperationNode::GeometricalOp::And, and_with, res_max)
else
res = res_max
end
end
return res
end
end
class DRCOpNodeBBoxParameterFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :parameter
attr_accessor :inverted
def initialize(engine, parameter, input, description)
super(engine)
self.parameter = parameter
self.input = input
self.inverted = false
self.description = description
end
def do_create_node(cache)
args = [ self.input.create_node(cache), self.inverse ]
args << (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
if self.lt || self.le
args << self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
end
RBA::CompoundRegionOperationNode::new_perimeter_filter(*args)
end
def inverted
res = self.dup
res.inverted = !res.inverted
return res
end
end
class DRCOpNodeCornersFilter < DRCOpNodeWithCompare
attr_accessor :input
attr_accessor :parameter
attr_accessor :inverted
def initialize(engine, as_dots, input)
super(engine)
self.as_dots = as_dots
self.input = input
self.description = "corners"
end
def do_create_node(cache)
args = [ self.input.create_node(cache) ]
angle_delta = 1e-6
args << (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0))
args << (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0))
if self.as_dots
RBA::CompoundRegionOperationNode::new_corners_as_dots_node(*args)
else
args << 2 # dimension is 2x2 DBU
RBA::CompoundRegionOperationNode::new_corners_as_rectangles_node(*args)
end
end
end
class DRCOpNodeRelativeExtents < DRCOpNode
attr_accessor :input
attr_accessor :as_edges, :fx1, :fx2, :fy1, :fy2, :dx, :dy
def initialize(engine, input, as_edges, fx1, fx2, fy1, fy2, dx = 0, dy = 0)
super(engine)
self.input = input
self.as_edges = as_edges
self.description = "extents"
end
def dump(indent)
if !self.as_edges
return "extents(%.12g,%.12g,%.12g,%.12g,%12g,%.12g)" % [self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy]
else
return "extents_as_edges(%.12g,%.12g,%.12g,%.12g)" % [self.fx1, self.fx2, self.fy1, self.fy2]
end
end
def do_create_node(cache)
if !self.as_edges
RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy)
else
RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2)
end
end
end
end

View File

@ -0,0 +1,372 @@
# $autorun-early
module DRC
class DRCLayer
# %DRC%
# @name drc
# @brief Universal DRC function
# @synopsis drc(...)
#
# TODO: add doc
def drc(op)
@engine._context("drc") do
requires_region
return DRCLayer::new(@engine, self.data.complex_op(op.create_node({})))
end
end
end
class DRCEngine
# %DRC%
# @name case
# @brief A conditional selector for the "drc" universal DRC function
# @synopsis case(...)
#
# This function provides a conditional selector for the "drc" function.
# It is used this way:
#
# @code
# out = in.drc(case(c1, r1, c2, r2, ..., cn, rn)
# out = in.drc(case(c1, r1, c2, r2, ..., cn, rn, rdef)
# @/code
#
# This function will evaluate c1 which is a universal DRC expression (see \drc).
# If the result is not empty, "case" will evaluate and return r1. Otherwise it
# will continue with c2 and the result of this expression is not empty it will
# return r2. Otherwise it will continue with c3/r3 etc.
#
# If an odd number of arguments is given, the last expression is evaluated if
# none of the conditions c1..cn gives a non-empty result.
#
# As a requirement, the result types of all r1..rn expressions and the rdef
# needs to be the same - i.e. all need to render polygons or edges or edge pairs.
def case(*args)
self._context("case") do
anum = 1
types = []
args.each do |a|
if !a.is_a?(DRCOpNode)
raise("All inputs need to be valid compound operation expressions (argument ##{anum} isn't)")
end
if a % 2 == 0
types << a.result_type
end
anum += 1
end
if types.sort.uniq.size > 1
raise("All result arguments need to have the same type (we got '" + types.collect(:to_s).join(",") + "')")
end
DRCOpNodeCase::new(self, args)
end
end
# %DRC%
# @name secondary
# @brief Provides secondary input for the "drc" universal DRC function
# @synopsis secondary(layer)
#
# To supply additional input for the universal DRC expressions (see \drc), use
# "secondary" with a layer argument. This example provides a boolean AND
# between l1 and l2:
#
# @code
# l1 = layer(1, 0)
# l2 = layer(2, 0)
# out = l1.drc(primary & secondary(l2))
# @/code
def secondary(layer)
self._context("secondary") do
layer.requires_region
res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(layer.data))
res.description = "secondary"
return res
end
end
# %DRC%
# @name primary
# @brief Represents the primary input of the universal DRC function
# @synopsis primary
#
# The primary input of the universal DRC function is the layer the \drc function
# is called on.
def primary
res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_primary)
res.description = "primary"
return res
end
# %DRC%
# @name if_all
# @brief Evaluates to the primary shape when all condition expression results are non-empty
# @synopsis if_all(c1, ... cn)
#
# This function will evaluate the conditions c1 to cn and return the
# current primary shape if all conditions render a non-empty result.
# The following example selects all shapes which are rectangles and
# whose area is larger than 0.5 square micrometers:
#
# @code
# out = in.drc(if_all(area > 0.5, rectangle))
# @/code
#
# The condition expressions can be of any type (edges, edge pairs and polygons).
# %DRC%
# @name if_any
# @brief Evaluates to the primary shape when any condition expression results is non-empty
# @synopsis if_any(c1, ... cn)
#
# This function will evaluate the conditions c1 to cn and return the
# current primary shape if at least one condition renders a non-empty result.
# See \if_all for an example how to use the if_... functions.
# %DRC%
# @name if_none
# @brief Evaluates to the primary shape when all of the condition expression results are empty
# @synopsis if_none(c1, ... cn)
#
# This function will evaluate the conditions c1 to cn and return the
# current primary shape if all conditions renders an empty result.
# See \if_all for an example how to use the if_... functions.
%w(
if_all
if_any
if_none
).each do |f|
eval <<"CODE"
def #{f}(*args)
self._context("#{f}") do
args.each_with_index do |a,ia|
if ! a.is_a?(DRCOpNode)
raise("Argument #" + (ia + 1).to_s + " to #{f} must be a DRC expression")
end
end
res = DRCOpNodeLogicalBool::new(self, :#{f})
res.children = args
res
end
end
CODE
end
# %DRC%
# @name bbox_height
# @brief Selects primary shapes based on their bounding box height
# @synopsis bbox_height (in condition)
#
# This method creates a universal DRC expression (see \drc) to select primary shapes whose
# bounding box height satisfies the condition. Conditions can be written as arithmetic comparisons
# against numeric values. For example, "bbox_height < 2.0" will select all primary shapes whose
# bounding box height is less than 2 micrometers. See \drc for more details about comparison
# specs.
# %DRC%
# @name bbox_width
# @brief Selects primary shapes based on their bounding box width
# @synopsis bbox_max (in condition)
#
# See \bbox_height for more details.
# %DRC%
# @name bbox_max
# @brief Selects primary shapes based on their bounding box height or width, whichever is larger
# @synopsis bbox_max (in condition)
#
# See \bbox_height for more details.
# %DRC%
# @name bbox_min
# @brief Selects primary shapes based on their bounding box height or width, whichever is smaller
# @synopsis bbox_max (in condition)
#
# See \bbox_height for more details.
%w(
bbox_height
bbox_max
bbox_min
bbox_width
).each do |f|
eval <<"CODE"
def #{f}
self._context("#{f}") do
primary.#{f}
end
end
CODE
end
%w(
area
holes
hulls
odd_polygons
perimeter
rectangles
rectilinear
).each do |f|
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
eval <<"CODE"
def _cop_#{f}
primary.#{f}
end
CODE
end
def _cop_corners(as_dots = DRCAsDots::new(false))
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
return primary.corners(as_dots)
end
%w(
extent_refs
extents
middle
rounded_corners
sized
smoothed
).each do |f|
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
eval <<"CODE"
def _cop_#{f}(*args)
primary.#{f}(*args)
end
CODE
end
%w(
covering
inside
interacting
outside
overlapping
).each do |f|
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
eval <<"CODE"
def _cop_#{f}(other)
primary.#{f}(other)
end
CODE
end
%w(
enclosing
isolated
notch
overlap
separation
space
width
).each do |f|
# NOTE: these methods are fallback for the respective global ones which route to DRCLayer or here.
eval <<"CODE"
def _cop_#{f}(*args)
metrics = RBA::Region::Euclidian
minp = nil
maxp = nil
alim = nil
whole_edges = false
other = nil
shielded = nil
opposite_filter = RBA::Region::NoOppositeFilter
rect_filter = RBA::Region::NoRectFilter
n = 1
args.each do |a|
if a.is_a?(DRCMetrics)
metrics = a.value
elsif a.is_a?(DRCWholeEdges)
whole_edges = a.value
elsif a.is_a?(DRCOppositeErrorFilter)
opposite_filter = a.value
elsif a.is_a?(DRCRectangleErrorFilter)
rect_filter = RBA::Region::RectFilter::new(a.value.to_i | rect_filter.to_i)
elsif a.is_a?(DRCAngleLimit)
alim = a.value
elsif a.is_a?(DRCOpNode)
other = a
elsif a.is_a?(DRCProjectionLimits)
minp = self._prep_value(a.min)
maxp = self._prep_value(a.max)
elsif a.is_a?(DRCShielded)
shielded = a.value
else
raise("Parameter #" + n.to_s + " does not have an expected type (is " + a.inspect + ")")
end
n += 1
end
args = [ whole_edges, metrics, alim, minp, maxp ]
args << (shielded == nil ? true : shielded)
if :#{f} != :width && :#{f} != :notch
args << opposite_filter
args << rect_filter
elsif opposite_filter != RBA::Region::NoOppositeFilter
raise("An opposite error filter cannot be used with this check")
elsif rect_filter != RBA::Region::NoRectFilter
raise("A rectangle error filter cannot be used with this check")
end
if :#{f} == :width || :#{f} == :space || :#{f} == :notch || :#{f} == :isolated
if other
raise("No other layer must be specified for a single-layer check")
end
else
if !other
raise("The other layer must be specified for a two-layer check")
end
end
DRCOpNodeCheck::new(self, :#{f}, other, *args)
end
CODE
end
def _cop_iso(*args)
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
_cop_isolated(*args)
end
def _cop_sep(*args)
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
_cop_separation(*args)
end
def _cop_enc(*args)
# NOTE: this method is a fallback for the respective global ones which route to DRCLayer or here.
_cop_separation(*args)
end
end
end

View File

@ -92,16 +92,22 @@ module DRC
end end
def projection_limits(*args) def projection_limits(*args)
self._context("projection_limits") do
DRCProjectionLimits::new(*args) DRCProjectionLimits::new(*args)
end end
end
def angle_limit(a) def angle_limit(a)
self._context("angle_limit") do
DRCAngleLimit::new(a) DRCAngleLimit::new(a)
end end
end
def whole_edges(f = true) def whole_edges(f = true)
self._context("whole_edges") do
DRCWholeEdges::new(f) DRCWholeEdges::new(f)
end end
end
def euclidian def euclidian
DRCMetrics::new(RBA::Region::Euclidian) DRCMetrics::new(RBA::Region::Euclidian)
@ -148,12 +154,16 @@ module DRC
end end
def pattern(p) def pattern(p)
self._context("pattern") do
DRCPattern::new(true, p) DRCPattern::new(true, p)
end end
end
def text(p) def text(p)
self._context("text") do
DRCPattern::new(false, p) DRCPattern::new(false, p)
end end
end
def as_dots def as_dots
DRCAsDots::new(true) DRCAsDots::new(true)
@ -168,16 +178,22 @@ module DRC
end end
def area_only(r) def area_only(r)
self._context("area_only") do
DRCAreaAndPerimeter::new(r, 1.0, 0.0) DRCAreaAndPerimeter::new(r, 1.0, 0.0)
end end
end
def perimeter_only(r, f) def perimeter_only(r, f)
self._context("perimeter_only") do
DRCAreaAndPerimeter::new(r, 0.0, f) DRCAreaAndPerimeter::new(r, 0.0, f)
end end
end
def area_and_perimeter(r, f) def area_and_perimeter(r, f)
self._context("area_and_perimeter") do
DRCAreaAndPerimeter::new(r, 1.0, f) DRCAreaAndPerimeter::new(r, 1.0, f)
end end
end
# %DRC% # %DRC%
# @brief Defines SPICE output format (with options) # @brief Defines SPICE output format (with options)
@ -190,6 +206,7 @@ module DRC
# information comments such as instance coordinates or pin names. # information comments such as instance coordinates or pin names.
def write_spice(use_net_names = nil, with_comments = nil) def write_spice(use_net_names = nil, with_comments = nil)
self._context("write_spice") do
writer = RBA::NetlistSpiceWriter::new writer = RBA::NetlistSpiceWriter::new
if use_net_names != nil if use_net_names != nil
writer.use_net_names = use_net_names writer.use_net_names = use_net_names
@ -199,6 +216,7 @@ module DRC
end end
writer writer
end end
end
# %DRC% # %DRC%
# @brief Supplies the MOS3 transistor extractor class # @brief Supplies the MOS3 transistor extractor class
@ -211,8 +229,10 @@ module DRC
# about this extractor (non-strict mode applies for 'mos3'). # about this extractor (non-strict mode applies for 'mos3').
def mos3(name) def mos3(name)
self._context("mos3") do
RBA::DeviceExtractorMOS3Transistor::new(name) RBA::DeviceExtractorMOS3Transistor::new(name)
end end
end
# %DRC% # %DRC%
# @brief Supplies the MOS4 transistor extractor class # @brief Supplies the MOS4 transistor extractor class
@ -225,8 +245,10 @@ module DRC
# about this extractor (non-strict mode applies for 'mos4'). # about this extractor (non-strict mode applies for 'mos4').
def mos4(name) def mos4(name)
self._context("mos4") do
RBA::DeviceExtractorMOS4Transistor::new(name) RBA::DeviceExtractorMOS4Transistor::new(name)
end end
end
# %DRC% # %DRC%
# @brief Supplies the DMOS3 transistor extractor class # @brief Supplies the DMOS3 transistor extractor class
@ -241,8 +263,10 @@ module DRC
# about this extractor (strict mode applies for 'dmos3'). # about this extractor (strict mode applies for 'dmos3').
def dmos3(name) def dmos3(name)
self._context("dmos3") do
RBA::DeviceExtractorMOS3Transistor::new(name, true) RBA::DeviceExtractorMOS3Transistor::new(name, true)
end end
end
# %DRC% # %DRC%
# @brief Supplies the MOS4 transistor extractor class # @brief Supplies the MOS4 transistor extractor class
@ -257,8 +281,10 @@ module DRC
# about this extractor (strict mode applies for 'dmos4'). # about this extractor (strict mode applies for 'dmos4').
def dmos4(name) def dmos4(name)
self._context("dmos4") do
RBA::DeviceExtractorMOS4Transistor::new(name, true) RBA::DeviceExtractorMOS4Transistor::new(name, true)
end end
end
# %DRC% # %DRC%
# @brief Supplies the BJT3 transistor extractor class # @brief Supplies the BJT3 transistor extractor class
@ -271,8 +297,10 @@ module DRC
# about this extractor. # about this extractor.
def bjt3(name) def bjt3(name)
self._context("bjt3") do
RBA::DeviceExtractorBJT3Transistor::new(name) RBA::DeviceExtractorBJT3Transistor::new(name)
end end
end
# %DRC% # %DRC%
# @brief Supplies the BJT4 transistor extractor class # @brief Supplies the BJT4 transistor extractor class
@ -285,8 +313,10 @@ module DRC
# about this extractor. # about this extractor.
def bjt4(name) def bjt4(name)
self._context("bjt4") do
RBA::DeviceExtractorBJT4Transistor::new(name) RBA::DeviceExtractorBJT4Transistor::new(name)
end end
end
# %DRC% # %DRC%
# @brief Supplies the diode extractor class # @brief Supplies the diode extractor class
@ -299,8 +329,10 @@ module DRC
# about this extractor. # about this extractor.
def diode(name) def diode(name)
self._context("diode") do
RBA::DeviceExtractorDiode::new(name) RBA::DeviceExtractorDiode::new(name)
end end
end
# %DRC% # %DRC%
# @brief Supplies the resistor extractor class # @brief Supplies the resistor extractor class
@ -315,8 +347,10 @@ module DRC
# about this extractor. # about this extractor.
def resistor(name, sheet_rho) def resistor(name, sheet_rho)
self._context("resistor") do
RBA::DeviceExtractorResistor::new(name, sheet_rho) RBA::DeviceExtractorResistor::new(name, sheet_rho)
end end
end
# %DRC% # %DRC%
# @brief Supplies the resistor extractor class that includes a bulk terminal # @brief Supplies the resistor extractor class that includes a bulk terminal
@ -330,8 +364,10 @@ module DRC
# about this extractor. # about this extractor.
def resistor_with_bulk(name, sheet_rho) def resistor_with_bulk(name, sheet_rho)
self._context("resistor_with_bulk") do
RBA::DeviceExtractorResistorWithBulk::new(name, sheet_rho) RBA::DeviceExtractorResistorWithBulk::new(name, sheet_rho)
end end
end
# %DRC% # %DRC%
# @brief Supplies the capacitor extractor class # @brief Supplies the capacitor extractor class
@ -344,8 +380,10 @@ module DRC
# about this extractor. # about this extractor.
def capacitor(name, area_cap) def capacitor(name, area_cap)
self._context("capacitor") do
RBA::DeviceExtractorCapacitor::new(name, area_cap) RBA::DeviceExtractorCapacitor::new(name, area_cap)
end end
end
# %DRC% # %DRC%
# @brief Supplies the capacitor extractor class that includes a bulk terminal # @brief Supplies the capacitor extractor class that includes a bulk terminal
@ -359,8 +397,10 @@ module DRC
# about this extractor. # about this extractor.
def capacitor_with_bulk(name, area_cap) def capacitor_with_bulk(name, area_cap)
self._context("capacitor_with_bulk") do
RBA::DeviceExtractorCapacitorWithBulk::new(name, area_cap) RBA::DeviceExtractorCapacitorWithBulk::new(name, area_cap)
end end
end
# %DRC% # %DRC%
# @name verbose? # @name verbose?
@ -458,7 +498,7 @@ module DRC
def use_dbu(d) def use_dbu(d)
if @dbu_read if @dbu_read
raise "Cannot change the database unit at this point" raise("Cannot change the database unit at this point")
end end
# Should have a "context", but no such thing for Float or Fixnum # Should have a "context", but no such thing for Float or Fixnum
1.0.class._dbu = d 1.0.class._dbu = d
@ -778,6 +818,8 @@ module DRC
def source(arg = nil, arg2 = nil) def source(arg = nil, arg2 = nil)
self._context("source") do
if arg if arg
if arg.is_a?(String) if arg.is_a?(String)
@ -796,7 +838,7 @@ module DRC
layout.read(arg) layout.read(arg)
cell = nil cell = nil
if arg2 if arg2
arg2.is_a?(String) || raise("Second argument of 'source' must be a string") arg2.is_a?(String) || raise("Second argument must be a string")
cell = layout.cell(arg2) cell = layout.cell(arg2)
cell || raise("Cell name #{arg2} not found in input layout") cell || raise("Cell name #{arg2} not found in input layout")
end end
@ -810,14 +852,14 @@ module DRC
cell = arg.cell(cell) cell = arg.cell(cell)
cell || raise("Cell name #{cell} not found in input layout") cell || raise("Cell name #{cell} not found in input layout")
elsif !cell.is_a?(RBA::Cell) elsif !cell.is_a?(RBA::Cell)
raise("Second argument of 'source' must be a string or RBA::Cell object") raise("Second argument must be a string or RBA::Cell object")
end end
@def_source = make_source(arg, cell) @def_source = make_source(arg, cell)
elsif arg.is_a?(RBA::Cell) elsif arg.is_a?(RBA::Cell)
@def_source = make_source(arg.layout, arg) @def_source = make_source(arg.layout, arg)
else else
raise("Invalid argument for 'source' method") raise("Invalid argument '" + arg.inspect + "'")
end end
else else
@ -837,6 +879,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name layout # @name layout
# @brief Specifies an additional layout for the input source. # @brief Specifies an additional layout for the input source.
@ -873,6 +917,8 @@ module DRC
def layout(arg = nil, arg2 = nil) def layout(arg = nil, arg2 = nil)
self._context("layout") do
if arg if arg
if arg.is_a?(String) if arg.is_a?(String)
@ -914,6 +960,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name report # @name report
# @brief Specifies a report database for output # @brief Specifies a report database for output
@ -936,6 +984,8 @@ module DRC
def report(description, filename = nil, cellname = nil) def report(description, filename = nil, cellname = nil)
self._context("report") do
@output_rdb_file = filename @output_rdb_file = filename
name = filename && File::basename(filename) name = filename && File::basename(filename)
@ -965,7 +1015,7 @@ module DRC
cn ||= source && source.cell_name cn ||= source && source.cell_name
cn ||= cellname cn ||= cellname
cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter of 'report'") cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter")
@output_rdb_cell = @output_rdb.create_cell(cn) @output_rdb_cell = @output_rdb.create_cell(cn)
@output_rdb.generator = self._generator @output_rdb.generator = self._generator
@ -974,6 +1024,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name report_netlist # @name report_netlist
# @brief Specifies an extracted netlist report for output # @brief Specifies an extracted netlist report for output
@ -989,12 +1041,18 @@ module DRC
# version of the L2N DB format will be used. # version of the L2N DB format will be used.
def report_netlist(filename = nil, long = nil) def report_netlist(filename = nil, long = nil)
self._context("report_netlist") do
@show_l2ndb = true @show_l2ndb = true
if filename if filename
filename.is_a?(String) || raise("Argument must be string in report_netlist") filename.is_a?(String) || raise("Argument must be string")
end end
@output_l2ndb_file = filename @output_l2ndb_file = filename
@output_l2ndb_long = long @output_l2ndb_long = long
end
end end
# %DRC% # %DRC%
@ -1011,16 +1069,22 @@ module DRC
# See \write_spice for more details. # See \write_spice for more details.
def target_netlist(filename, format = nil, comment = nil) def target_netlist(filename, format = nil, comment = nil)
filename.is_a?(String) || raise("First argument must be string in target_netlist")
self._context("target_netlist") do
filename.is_a?(String) || raise("First argument must be string")
@target_netlist_file = filename @target_netlist_file = filename
if format if format
format.is_a?(RBA::NetlistWriter) || raise("Second argument must be netlist writer object in target_netlist") format.is_a?(RBA::NetlistWriter) || raise("Second argument must be netlist writer object")
end end
@target_netlist_format = format @target_netlist_format = format
if comment if comment
comment.is_a?(String) || raise("Third argument must be string in target_netlist") comment.is_a?(String) || raise("Third argument must be string")
end end
@target_netlist_comment = comment @target_netlist_comment = comment
end
end end
# %DRC% # %DRC%
@ -1033,6 +1097,8 @@ module DRC
def output_cell(cellname) def output_cell(cellname)
self._context("output_cell") do
# finish what we got so far # finish what we got so far
_flush _flush
@ -1059,6 +1125,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name target # @name target
# @brief Specify the target layout # @brief Specify the target layout
@ -1086,6 +1154,8 @@ module DRC
def target(arg, cellname = nil) def target(arg, cellname = nil)
self._context("target") do
# finish what we got so far # finish what we got so far
_finish(false) _finish(false)
@ -1126,7 +1196,9 @@ module DRC
@output_layout_file = nil @output_layout_file = nil
else else
raise("Invalid argument for 'target' method") raise("Invalid argument '" + arg.inspect + "'")
end
end end
end end
@ -1139,8 +1211,10 @@ module DRC
# RBA::DBox constructors. # RBA::DBox constructors.
def box(*args) def box(*args)
self._context("box") do
RBA::DBox::new(*args) RBA::DBox::new(*args)
end end
end
# %DRC% # %DRC%
# @name path # @name path
@ -1150,8 +1224,10 @@ module DRC
# RBA::DPath constructors. # RBA::DPath constructors.
def path(*args) def path(*args)
self._context("path") do
RBA::DPath::new(*args) RBA::DPath::new(*args)
end end
end
# %DRC% # %DRC%
# @name polygon # @name polygon
@ -1161,8 +1237,10 @@ module DRC
# RBA::DPolygon constructors. # RBA::DPolygon constructors.
def polygon(*args) def polygon(*args)
self._context("polygon") do
RBA::DPolygon::new(*args) RBA::DPolygon::new(*args)
end end
end
# %DRC% # %DRC%
# @name p # @name p
@ -1177,8 +1255,10 @@ module DRC
# @/code # @/code
def p(x, y) def p(x, y)
self._context("p") do
RBA::DPoint::new(x, y) RBA::DPoint::new(x, y)
end end
end
# %DRC% # %DRC%
# @name edge # @name edge
@ -1188,8 +1268,10 @@ module DRC
# RBA::DEdge constructors. # RBA::DEdge constructors.
def edge(*args) def edge(*args)
self._context("edge") do
RBA::DEdge::new(*args) RBA::DEdge::new(*args)
end end
end
# %DRC% # %DRC%
# @name extent # @name extent
@ -1198,8 +1280,10 @@ module DRC
# See \Source#extent for a description of that function. # See \Source#extent for a description of that function.
def extent def extent
self._context("extent") do
layout.extent layout.extent
end end
end
# %DRC% # %DRC%
# @name input # @name input
@ -1209,48 +1293,44 @@ module DRC
# polygons and labels. See \polygons and \labels for more specific versions of # polygons and labels. See \polygons and \labels for more specific versions of
# this method. # this method.
def input(*args)
layout.input(*args)
end
# %DRC% # %DRC%
# @name polygons # @name polygons
# @brief Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source # @brief Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source
# @synopsis polygons(args) # @synopsis polygons(args)
# See \Source#polygons for a description of that function. # See \Source#polygons for a description of that function.
def polygons(*args)
layout.polygons(*args)
end
# %DRC% # %DRC%
# @name labels # @name labels
# @brief Gets the labels (text) from an original layer # @brief Gets the labels (text) from an original layer
# @synopsis labels(args) # @synopsis labels(args)
# See \Source#labels for a description of that function. # See \Source#labels for a description of that function.
def labels(*args)
layout.labels(*args)
end
# %DRC% # %DRC%
# @name edges # @name edges
# @brief Gets the edges from an original layer # @brief Gets the edges from an original layer
# @synopsis edges(args) # @synopsis edges(args)
# See \Source#edges for a description of that function. # See \Source#edges for a description of that function.
def edges(*args)
layout.edges(*args)
end
# %DRC% # %DRC%
# @name edge_pairs # @name edge_pairs
# @brief Gets the edges from an original layer # @brief Gets the edges from an original layer
# @synopsis edge_pairs(args) # @synopsis edge_pairs(args)
# See \Source#edge_pairs for a description of that function. # See \Source#edge_pairs for a description of that function.
def edge_pairs(*args) %w(
layout.edge_pairs(*args) edge_pairs
edges
input
labels
polygons
).each do |f|
eval <<"CODE"
def #{f}(*args)
self._context("#{f}") do
layout.#{f}(*args)
end
end
CODE
end end
# %DRC% # %DRC%
@ -1260,8 +1340,10 @@ module DRC
# This function is equivalent to "layer.output(args)". See \Layer#output for details about this function. # This function is equivalent to "layer.output(args)". See \Layer#output for details about this function.
def output(layer, *args) def output(layer, *args)
self._context("output") do
layer.output(*args) layer.output(*args)
end end
end
# %DRC% # %DRC%
# @name layers # @name layers
@ -1290,8 +1372,10 @@ module DRC
# @/code # @/code
def cell(*args) def cell(*args)
self._context("cell") do
@def_source = layout.cell(*args) @def_source = layout.cell(*args)
output_cell(*args) output_cell(*args)
end
nil nil
end end
@ -1302,7 +1386,9 @@ module DRC
# See \Source#select for a description of that function. # See \Source#select for a description of that function.
def select(*args) def select(*args)
self._context("select") do
@def_source = layout.select(*args) @def_source = layout.select(*args)
end
nil nil
end end
@ -1322,7 +1408,9 @@ module DRC
# @/code # @/code
def clip(*args) def clip(*args)
self._context("clip") do
@def_source = layout.clip(*args) @def_source = layout.clip(*args)
end
nil nil
end end
@ -1417,6 +1505,7 @@ module DRC
# Cheats have been introduced in version 0.26.1. # Cheats have been introduced in version 0.26.1.
def cheat(*args, &block) def cheat(*args, &block)
self._wrapper_context("cheat") do
if _dss if _dss
_dss.push_state _dss.push_state
args.flatten.each { |a| _dss.add_breakout_cells(a.to_s) } args.flatten.each { |a| _dss.add_breakout_cells(a.to_s) }
@ -1427,31 +1516,134 @@ module DRC
end end
ret ret
end end
end
# make some DRCLayer methods available as functions # make some DRCLayer methods available as functions
# for the engine # for the engine
%w(join and or xor not %w(
in touching overlapping inside outside interacting and
select_touching select_overlapping select_inside select_outside select_interacting andnot
merge merged rectangles rectilinear non_rectangles non_rectilinear area
with_area with_perimeter with_angle with_length with_bbox_width with_bbox_area with_bbox_height with_bbox_min with_bbox_max
without_area without_perimeter without_length without_angle without_bbox_width without_bbox_area without_bbox_height without_bbox_min without_bbox_max
bbox bbox
area length perimeter centers
is_box? is_empty? is_merged? is_clean? is_raw? polygons? edges? edge_pairs? corners
strict non_strict is_strict? covering
centers end_segments start_segments enc
extended extended_in extended_out enclosing
extents hulls holes end_segments
scaled scale rotated rotate extended
move moved transform transformed extended_in
width space notch isolated overlap extended_out
size sized extent_refs
rounded_corners odd_polygons).each do |f| extents
first_edges
flatten
holes
hulls
in
inside
inside_part
interacting
intersections
iso
isolated
join
length
merge
merged
middle
move
moved
non_rectangles
non_rectilinear
non_strict
not
notch
not_covering
not_in
not_inside
not_interacting
not_outside
not_overlapping
odd_polygons
ongrid
or
output
outside
outside_part
overlap
overlapping
perimeter
pull_inside
pull_interacting
pull_overlapping
rectangles
rectilinear
rotate
rotated
rounded_corners
scale
scaled
second_edges
select_covering
select_inside
select_interacting
select_not_covering
select_not_inside
select_not_interacting
select_not_outside
select_not_overlapping
select_outside
select_overlapping
select_touching
sep
separation
size
sized
smoothed
snap
snapped
space
start_segments
strict
texts
texts_not
touching
transform
transformed
width
with_angle
with_area
with_bbox_area
with_bbox_height
with_bbox_max
with_bbox_min
with_bbox_width
with_length
without_angle
without_area
without_bbox
without_bbox_height
without_bbox_max
without_bbox_min
without_length
without_perimeter
with_perimeter
xor
).each do |f|
eval <<"CODE" eval <<"CODE"
def #{f}(*args) def #{f}(*args)
self._context("#{f}") do
if args[0].is_a?(DRCLayer)
obj = args.shift obj = args.shift
obj.#{f}(*args) return obj.#{f}(*args)
elsif self.respond_to?(:_cop_#{f})
# forward to _cop_ implementation for complex DRC operations
return self._cop_#{f}(*args)
else
raise("Function requires at a layer expression for the first argument")
end
end
end end
CODE CODE
end end
@ -1463,8 +1655,10 @@ CODE
# See \Netter# for more details # See \Netter# for more details
def netter def netter
self._context("netter") do
DRC::DRCNetter::new DRC::DRCNetter::new
end end
end
# %DRC% # %DRC%
# @name connect # @name connect
@ -1523,11 +1717,23 @@ CODE
# yet, this method will trigger the extraction process. # yet, this method will trigger the extraction process.
# See \Netter#netlist for a description of this function. # See \Netter#netlist for a description of this function.
%w(connect connect_global clear_connections connect_implicit antenna_check l2n_data device_scaling extract_devices netlist).each do |f| %w(
antenna_check
clear_connections
connect
connect_global
connect_implicit
device_scaling
extract_devices
l2n_data
netlist
).each do |f|
eval <<"CODE" eval <<"CODE"
def #{f}(*args) def #{f}(*args)
self._context("#{f}") do
_netter.#{f}(*args) _netter.#{f}(*args)
end end
end
CODE CODE
end end
@ -1542,6 +1748,22 @@ CODE
end end
end end
def _wrapper_context(func, *args, &proc)
begin
return yield(*args)
rescue => ex
raise("'" + func + "': " + ex.to_s)
end
end
def _context(func, *args, &proc)
begin
return yield(*args)
rescue => ex
raise("'" + func + "': " + ex.to_s)
end
end
def run_timed(desc, obj) def run_timed(desc, obj)
info(desc) info(desc)
@ -2087,7 +2309,7 @@ CODE
if @output_rdb if @output_rdb
if args.size < 1 if args.size < 1
raise("Invalid number of arguments for 'output' on report - category name and optional description expected") raise("Invalid number of arguments - category name and optional description expected")
end end
cat = @output_rdb.create_category(args[0].to_s) cat = @output_rdb.create_category(args[0].to_s)
@ -2121,12 +2343,12 @@ CODE
elsif args[0].is_a?(String) elsif args[0].is_a?(String)
info = RBA::LayerInfo::from_string(args[0]) info = RBA::LayerInfo::from_string(args[0])
else else
raise("Invalid parameter type for 'output' - must be string or number") raise("Invalid parameter type - must be string or number")
end end
elsif args.size == 2 || args.size == 3 elsif args.size == 2 || args.size == 3
info = RBA::LayerInfo::new(*args) info = RBA::LayerInfo::new(*args)
else else
raise("Invalid number of arguments for 'output' - one, two or three arguments expected") raise("Invalid number of arguments - one, two or three arguments expected")
end end
li = output.find_layer(info) li = output.find_layer(info)
if !li if !li

File diff suppressed because it is too large Load Diff

View File

@ -98,10 +98,12 @@ module DRC
def connect(a, b) def connect(a, b)
a.is_a?(DRC::DRCLayer) || raise("First argument of Netter#connect must be a layer") @engine._context("connect") do
b.is_a?(DRC::DRCLayer) || raise("Second argument of Netter#connect must be a layer")
a.requires_texts_or_region("Netter#connect (first argument)") a.is_a?(DRC::DRCLayer) || raise("First argument must be a layer")
b.requires_texts_or_region("Netter#connect (second argument)") b.is_a?(DRC::DRCLayer) || raise("Second argument must be a layer")
a.requires_texts_or_region
b.requires_texts_or_region
register_layer(a.data) register_layer(a.data)
register_layer(b.data) register_layer(b.data)
@ -111,6 +113,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name connect_global # @name connect_global
# @brief Connects a layer with a global net # @brief Connects a layer with a global net
@ -122,8 +126,10 @@ module DRC
def connect_global(l, name) def connect_global(l, name)
l.is_a?(DRC::DRCLayer) || raise("Layer argument of Netter#connect_global must be a layer") @engine._context("connect_global") do
l.requires_texts_or_region("Netter#connect_global (layer argument)")
l.is_a?(DRC::DRCLayer) || raise("Layer argument must be a layer")
l.requires_texts_or_region
register_layer(l.data) register_layer(l.data)
l.data.is_a?(RBA::Region) && @l2n.connect(l.data) l.data.is_a?(RBA::Region) && @l2n.connect(l.data)
@ -131,6 +137,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name extract_devices # @name extract_devices
# @brief Extracts devices based on the given extractor class, name and device layer selection # @brief Extracts devices based on the given extractor class, name and device layer selection
@ -186,16 +194,18 @@ module DRC
def extract_devices(devex, layer_selection) def extract_devices(devex, layer_selection)
@engine._context("extract_devices") do
ensure_data ensure_data
devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance in the two-arguments form") devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of must be a device extractor instance in the two-arguments form")
layer_selection.is_a?(Hash) || raise("Second argument of Netter#extract_devices must be a hash") layer_selection.is_a?(Hash) || raise("Second argument must be a hash")
ls = {} ls = {}
layer_selection.keys.sort.each do |n| layer_selection.keys.sort.each do |n|
l = layer_selection[n] l = layer_selection[n]
l.requires_texts_or_region("Netter#extract_devices (#{n} layer)") l.requires_texts_or_region
register_layer(l.data) register_layer(l.data)
ls[n.to_s] = l.data ls[n.to_s] = l.data
end end
@ -204,6 +214,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name device_scaling # @name device_scaling
# @brief Specifies a dimension scale factor for the geometrical device properties # @brief Specifies a dimension scale factor for the geometrical device properties
@ -213,9 +225,11 @@ module DRC
# does not correspond to the physical dimensions. # does not correspond to the physical dimensions.
def device_scaling(factor) def device_scaling(factor)
@engine._context("device_scaling") do
@device_scaling = factor @device_scaling = factor
@l2n && @l2n.device_scaling = factor @l2n && @l2n.device_scaling = factor
end end
end
# %DRC% # %DRC%
# @name clear_connections # @name clear_connections
@ -254,16 +268,22 @@ module DRC
# on "clear_connections". # on "clear_connections".
def connect_implicit(arg1, arg2 = nil) def connect_implicit(arg1, arg2 = nil)
@engine._context("connect_implicit") do
cleanup cleanup
if arg2 if arg2
(arg2.is_a?(String) && arg2 != "") || raise("The second argument of 'connect_implicit' has to be a non-empty string") (arg2.is_a?(String) && arg2 != "") || raise("The second argument has to be a non-empty string")
arg1.is_a?(String) || raise("The first argument of 'connect_implicit' has to be a string") arg1.is_a?(String) || raise("The first argument has to be a string")
@connect_implicit_per_cell[arg1] ||= [] @connect_implicit_per_cell[arg1] ||= []
@connect_implicit_per_cell[arg1] << arg2 @connect_implicit_per_cell[arg1] << arg2
else else
arg1.is_a?(String) || raise("The argument of 'connect_implicit' has to be a string") arg1.is_a?(String) || raise("The argument has to be a string")
@connect_implicit << arg1 @connect_implicit << arg1
end end
end
end end
# %DRC% # %DRC%
@ -388,6 +408,8 @@ module DRC
def antenna_check(agate, ametal, ratio, *diodes) def antenna_check(agate, ametal, ratio, *diodes)
@engine._context("antenna_check") do
gate_perimeter_factor = 0.0 gate_perimeter_factor = 0.0
gate_area_factor = 1.0 gate_area_factor = 1.0
if agate.is_a?(DRC::DRCLayer) if agate.is_a?(DRC::DRCLayer)
@ -397,13 +419,13 @@ module DRC
gate_perimeter_factor = agate.perimeter_factor gate_perimeter_factor = agate.perimeter_factor
gate_area_factor = agate.area_factor gate_area_factor = agate.area_factor
if ! gate.is_a?(DRC::DRCLayer) if ! gate.is_a?(DRC::DRCLayer)
raise("gate with area or area_and_perimeter: input argument must be a layer") raise("Gate with area or area_and_perimeter: input argument must be a layer")
end end
else else
raise("gate argument of Netter#antenna_check must be a layer ") raise("Gate argument must be a layer ")
end end
gate.requires_region("Netter#antenna_check (gate argument)") gate.requires_region
metal_perimeter_factor = 0.0 metal_perimeter_factor = 0.0
metal_area_factor = 1.0 metal_area_factor = 1.0
@ -414,25 +436,25 @@ module DRC
metal_perimeter_factor = ametal.perimeter_factor metal_perimeter_factor = ametal.perimeter_factor
metal_area_factor = ametal.area_factor metal_area_factor = ametal.area_factor
if ! metal.is_a?(DRC::DRCLayer) if ! metal.is_a?(DRC::DRCLayer)
raise("metal with area or area_and_perimeter: input argument must be a layer") raise("Metal with area or area_and_perimeter: input argument must be a layer")
end end
else else
raise("metal argument of Netter#antenna_check must be a layer") raise("Metal argument must be a layer")
end end
metal.requires_region("Netter#antenna_check (metal argument)") metal.requires_region
if !ratio.is_a?(1.class) && !ratio.is_a?(Float) if !ratio.is_a?(1.class) && !ratio.is_a?(Float)
raise("ratio argument Netter#antenna_check is not a number") raise("Ratio argument is not a number")
end end
dl = diodes.collect do |d| dl = diodes.collect do |d|
if d.is_a?(Array) if d.is_a?(Array)
d.size == 2 || raise("diode specification pair expects two elements") d.size == 2 || raise("Diode specification pair expects two elements")
d[0].requires_region("Netter#antenna_check (diode layer)") d[0].requires_region
[ d[0].data, d[1].to_f ] [ d[0].data, d[1].to_f ]
else else
d.requires_region("Netter#antenna_check (diode layer)") d.requires_region
[ d.data, 0.0 ] [ d.data, 0.0 ]
end end
end end
@ -441,6 +463,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name l2n_data # @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object # @brief Gets the internal RBA::LayoutToNetlist object
@ -450,6 +474,8 @@ module DRC
def l2n_data def l2n_data
@engine._context("l2n_data") do
ensure_data ensure_data
# run extraction in a timed environment # run extraction in a timed environment
@ -473,6 +499,8 @@ module DRC
end end
end
# %DRC% # %DRC%
# @name netlist # @name netlist
# @brief Gets the extracted netlist or triggers extraction if not done yet # @brief Gets the extracted netlist or triggers extraction if not done yet
@ -482,8 +510,10 @@ module DRC
# calls must have been made before this method is used. Further \connect # calls must have been made before this method is used. Further \connect
# statements will clear the netlist and re-extract it again. # statements will clear the netlist and re-extract it again.
def netlist def netlist
@engine._context("netlist") do
l2n_data && @l2n.netlist l2n_data && @l2n.netlist
end end
end
def _finish def _finish
clear_connections clear_connections

View File

@ -78,20 +78,27 @@ module DRC
end end
def set_box(method, *args) def set_box(method, *args)
@engine._context(method) do
box = nil box = nil
if args.size == 1 if args.size == 1
box = args[0] box = args[0]
box.is_a?(RBA::DBox) || raise("'#{method}' method requires a box specification") box.is_a?(RBA::DBox) || raise("Method requires a box specification")
elsif args.size == 2 elsif args.size == 2
(args[0].is_a?(RBA::DPoint) && args[1].is_a?(RBA::DPoint)) || raise("'#{method}' method requires a box specification with two points") (args[0].is_a?(RBA::DPoint) && args[1].is_a?(RBA::DPoint)) || raise("Method requires a box specification with two points")
box = RBA::DBox::new(args[0], args[1]) box = RBA::DBox::new(args[0], args[1])
elsif args.size == 4 elsif args.size == 4
box = RBA::DBox::new(*args) box = RBA::DBox::new(*args)
else else
raise("Invalid number of arguments for '#{method}' method") raise("Invalid number of arguments (1, 2 or 4 expected)")
end end
@box = RBA::Box::from_dbox(box * (1.0 / @layout.dbu)) @box = RBA::Box::from_dbox(box * (1.0 / @layout.dbu))
self self
end
end end
def inplace_clip(*args) def inplace_clip(*args)
@ -113,18 +120,22 @@ module DRC
end end
def inplace_cell(arg) def inplace_cell(arg)
@engine._context("inplace_cell") do
@cell = layout.cell(arg) @cell = layout.cell(arg)
@cell ||= layout.create_cell(arg) @cell ||= layout.create_cell(arg)
self self
end end
end
def inplace_select(*args) def inplace_select(*args)
@engine._context("inplace_select") do
args.each do |a| args.each do |a|
a.is_a?(String) || raise("Invalid arguments to 'select' method - must be strings") a.is_a?(String) || raise("Invalid arguments - must be strings")
@sel.push(a) @sel.push(a)
end end
self self
end end
end
# %DRC% # %DRC%
# @name select # @name select
@ -238,10 +249,12 @@ module DRC
%w(select cell clip touching overlapping).each do |f| %w(select cell clip touching overlapping).each do |f|
eval <<"CODE" eval <<"CODE"
def #{f}(*args) def #{f}(*args)
@engine._context("#{f}") do
s = self.dup s = self.dup
s.inplace_#{f}(*args) s.inplace_#{f}(*args)
s s
end end
end
CODE CODE
end end
@ -256,6 +269,7 @@ CODE
# @/code # @/code
def extent def extent
@engine._context("extent") do
layer = input layer = input
if @box if @box
layer.insert(RBA::DBox::from_ibox(@box) * @layout.dbu) layer.insert(RBA::DBox::from_ibox(@box) * @layout.dbu)
@ -264,6 +278,7 @@ CODE
end end
layer layer
end end
end
# %DRC% # %DRC%
# @name input # @name input
@ -309,9 +324,11 @@ CODE
# Use the global version of "input" without a source object to address the default source. # Use the global version of "input" without a source object to address the default source.
def input(*args) def input(*args)
@engine._context("input") do
layers = parse_input_layers(*args) layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region)) DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, RBA::Region))
end end
end
# %DRC% # %DRC%
# @name labels # @name labels
@ -331,9 +348,11 @@ CODE
# Use the global version of "labels" without a source object to address the default source. # Use the global version of "labels" without a source object to address the default source.
def labels(*args) def labels(*args)
@engine._context("labels") do
layers = parse_input_layers(*args) layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, RBA::Texts)) DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts, RBA::Texts))
end end
end
# %DRC% # %DRC%
# @name polygons # @name polygons
@ -352,9 +371,11 @@ CODE
# Use the global version of "polygons" without a source object to address the default source. # Use the global version of "polygons" without a source object to address the default source.
def polygons(*args) def polygons(*args)
@engine._context("polygons") do
layers = parse_input_layers(*args) layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, RBA::Region)) DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs, RBA::Region))
end end
end
# %DRC% # %DRC%
# @name edges # @name edges
@ -376,9 +397,11 @@ CODE
# This method has been introduced in version 0.27. # This method has been introduced in version 0.27.
def edges(*args) def edges(*args)
@engine._context("edges") do
layers = parse_input_layers(*args) layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, RBA::Edges)) DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SBoxes | RBA::Shapes::SPaths | RBA::Shapes::SPolygons | RBA::Shapes::SEdgePairs | RBA::Shapes::SEdges, RBA::Edges))
end end
end
# %DRC% # %DRC%
# @name edge_pairs # @name edge_pairs
@ -400,9 +423,11 @@ CODE
# This method has been introduced in version 0.27. # This method has been introduced in version 0.27.
def edge_pairs(*args) def edge_pairs(*args)
@engine._context("edge_pairs") do
layers = parse_input_layers(*args) layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, RBA::EdgePairs)) DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SEdgePairs, RBA::EdgePairs))
end end
end
# %DRC% # %DRC%
# @name make_layer # @name make_layer

View File

@ -6,6 +6,8 @@
<file alias="_drc_patch.rb">built-in-macros/_drc_patch.rb</file> <file alias="_drc_patch.rb">built-in-macros/_drc_patch.rb</file>
<file alias="_drc_source.rb">built-in-macros/_drc_source.rb</file> <file alias="_drc_source.rb">built-in-macros/_drc_source.rb</file>
<file alias="_drc_tags.rb">built-in-macros/_drc_tags.rb</file> <file alias="_drc_tags.rb">built-in-macros/_drc_tags.rb</file>
<file alias="_drc_complex_ops.rb">built-in-macros/_drc_complex_ops.rb</file>
<file alias="_drc_cop_integration.rb">built-in-macros/_drc_cop_integration.rb</file>
<file alias="drc_interpreters.lym">built-in-macros/drc_interpreters.lym</file> <file alias="drc_interpreters.lym">built-in-macros/drc_interpreters.lym</file>
<file alias="drc_install.lym">built-in-macros/drc_install.lym</file> <file alias="drc_install.lym">built-in-macros/drc_install.lym</file>
</qresource> </qresource>

View File

@ -21,7 +21,7 @@ class DRCLayer
def drc(op) def drc(op)
requires_region("drc") requires_region("drc")
return DRCLayer::new(@engine, self.data.complex_op(op.create_node)) return DRCLayer::new(@engine, self.data.complex_op(op.create_node({})))
end end
end end
@ -37,7 +37,16 @@ class DRCOpNode
self.description = "Basic" self.description = "Basic"
end end
def create_node def create_node(cache)
n = cache[self.object_id]
if !n
n = self.do_create_node(cache)
cache[self.object_id] = n
end
n
end
def do_create_node(cache)
@node @node
end end
@ -342,9 +351,9 @@ class DRCOpNodeLogicalBool &lt; DRCOpNode
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n") return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
end end
def create_node def do_create_node(cache)
log_op = { :land =&gt; RBA::CompoundRegionOperationNode::LogicalOp::LogAnd, :lor =&gt; RBA::CompoundRegionOperationNode::LogicalOp::LogOr }[self.op] log_op = { :land =&gt; RBA::CompoundRegionOperationNode::LogicalOp::LogAnd, :lor =&gt; RBA::CompoundRegionOperationNode::LogicalOp::LogOr }[self.op]
RBA::CompoundRegionOperationNode::new_logical_boolean(log_op, false, self.children.collect { |c| c.create_node }) RBA::CompoundRegionOperationNode::new_logical_boolean(log_op, false, self.children.collect { |c| c.create_node(cache) })
end end
end end
@ -365,14 +374,14 @@ class DRCOpNodeBool &lt; DRCOpNode
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n") return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
end end
def create_node def do_create_node(cache)
bool_op = { :&amp; =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::And, bool_op = { :&amp; =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::And,
:+ =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Or, :+ =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Or,
:| =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Or, :| =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Or,
:- =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Not, :- =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Not,
:^ =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Xor }[self.op] :^ =&gt; RBA::CompoundRegionOperationNode::GeometricalOp::Xor }[self.op]
nodes = self.children.collect do |c| nodes = self.children.collect do |c|
n = c.create_node n = c.create_node(cache)
if n.result_type == RBA::CompoundRegionOperationNode::ResultType::EdgePairs if n.result_type == RBA::CompoundRegionOperationNode::ResultType::EdgePairs
n = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(n) n = RBA::CompoundRegionOperationNode::new_edge_pair_to_first_edges(n)
end end
@ -397,8 +406,8 @@ class DRCOpNodeCase &lt; DRCOpNode
return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n") return indent + self.description + "\n" + self.children.collect { |c| c.dump(" " + indent) }.join("\n")
end end
def create_node def do_create_node(cache)
RBA::CompoundRegionOperationNode::new_case(self.children.collect { |c| c.create_node }) RBA::CompoundRegionOperationNode::new_case(self.children.collect { |c| c.create_node(cache) })
end end
end end
@ -556,8 +565,8 @@ class DRCOpNodeAreaFilter &lt; DRCOpNodeWithCompare
self.inverted ? "area" : "not_area" self.inverted ? "area" : "not_area"
end end
def create_node def do_create_node(cache)
args = [ self.input.create_node, self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args &lt;&lt; (self.gt ? make_area_value(self.gt) + 1 : (self.ge ? make_area_value(self.ge) : 0)) args &lt;&lt; (self.gt ? make_area_value(self.gt) + 1 : (self.ge ? make_area_value(self.ge) : 0))
if self.lt || self.le if self.lt || self.le
args &lt;&lt; self.lt ? make_area_value(self.lt) : make_area_value(self.le) - 1 args &lt;&lt; self.lt ? make_area_value(self.lt) : make_area_value(self.le) - 1
@ -589,8 +598,8 @@ class DRCOpNodeEdgeLengthFilter &lt; DRCOpNodeWithCompare
self.inverted ? "length" : "not_length" self.inverted ? "length" : "not_length"
end end
def create_node def do_create_node(cache)
args = [ self.input.create_node, self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args &lt;&lt; (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0)) args &lt;&lt; (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
if self.lt || self.le if self.lt || self.le
args &lt;&lt; self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1 args &lt;&lt; self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
@ -622,8 +631,8 @@ class DRCOpNodeEdgeOrientationFilter &lt; DRCOpNodeWithCompare
self.inverted ? "angle" : "not_angle" self.inverted ? "angle" : "not_angle"
end end
def create_node def do_create_node(cache)
args = [ self.input.create_node, self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
angle_delta = 1e-6 angle_delta = 1e-6
args &lt;&lt; (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0)) args &lt;&lt; (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0))
args &lt;&lt; (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0)) args &lt;&lt; (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0))
@ -654,8 +663,8 @@ class DRCOpNodePerimeterFilter &lt; DRCOpNodeWithCompare
self.inverted ? "perimeter" : "not_perimeter" self.inverted ? "perimeter" : "not_perimeter"
end end
def create_node def do_create_node(cache)
args = [ self.input.create_node, self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args &lt;&lt; (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0)) args &lt;&lt; (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
if self.lt || self.le if self.lt || self.le
args &lt;&lt; self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1 args &lt;&lt; self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
@ -686,8 +695,8 @@ class DRCOpNodeInteractingWithCount &lt; DRCOpNodeWithCompare
self.description = (self.inverted ? "" : "not_") + self.op.to_s self.description = (self.inverted ? "" : "not_") + self.op.to_s
end end
def create_node def do_create_node(cache)
args = [ self.a.create_node, self.b.create_node, self.inverse ] args = [ self.a.create_node(cache), self.b.create_node(cache), self.inverse ]
args &lt;&lt; (self.gt ? self.gt + 1 : (self.ge ? self.ge : 0)) args &lt;&lt; (self.gt ? self.gt + 1 : (self.ge ? self.ge : 0))
if self.lt || self.le if self.lt || self.le
args &lt;&lt; self.lt ? self.lt : self.le - 1 args &lt;&lt; self.lt ? self.lt : self.le - 1
@ -721,10 +730,10 @@ class DRCOpNodeInteracting &lt; DRCOpNode
self.description = (self.inverted ? "" : "not_") + self.op.to_s self.description = (self.inverted ? "" : "not_") + self.op.to_s
end end
def create_node def do_create_node(cache)
factory = { :inside =&gt; :new_inside, factory = { :inside =&gt; :new_inside,
:outside =&gt; :new_outside }[self.op] :outside =&gt; :new_outside }[self.op]
RBA::CompoundRegionOperationNode::send(factory, self.a.create_node, self.b.create_node, self.inverse) RBA::CompoundRegionOperationNode::send(factory, self.a.create_node(cache), self.b.create_node(cache), self.inverse)
end end
def inverted def inverted
@ -741,7 +750,7 @@ class DRCOpNodeFilter &lt; DRCOpNode
attr_accessor :factory attr_accessor :factory
attr_accessor :args attr_accessor :args
def initialize(engine, input, factory, description, args = []) def initialize(engine, input, factory, description, *args)
super(engine) super(engine)
self.input = input self.input = input
self.factory = factory self.factory = factory
@ -757,8 +766,8 @@ class DRCOpNodeFilter &lt; DRCOpNode
end end
end end
def create_node def do_create_node(cache)
RBA::CompoundRegionOperationNode::send(self.factory, self.input.create_node, *args) RBA::CompoundRegionOperationNode::send(self.factory, self.input.create_node(cache), *args)
end end
end end
@ -785,7 +794,7 @@ class DRCOpNodeCheck &lt; DRCOpNodeWithCompare
end end
end end
def create_node def do_create_node(cache)
if !(self.lt || self.le) &amp;&amp; !(self.gt || self.ge) if !(self.lt || self.le) &amp;&amp; !(self.gt || self.ge)
raise("No value given for check #{self.check}") raise("No value given for check #{self.check}")
@ -840,8 +849,8 @@ class DRCOpNodeBBoxParameterFilter &lt; DRCOpNodeWithCompare
self.description = description self.description = description
end end
def create_node def do_create_node(cache)
args = [ self.input.create_node, self.inverse ] args = [ self.input.create_node(cache), self.inverse ]
args &lt;&lt; (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0)) args &lt;&lt; (self.gt ? self._make_value(self.gt, :gt) + 1 : (self.ge ? self._make_value(self.ge, :ge) : 0))
if self.lt || self.le if self.lt || self.le
args &lt;&lt; self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1 args &lt;&lt; self.lt ? self._make_value(self.lt, :lt) : self._make_value(self.le, :le) - 1
@ -870,8 +879,8 @@ class DRCOpNodeCornersFilter &lt; DRCOpNodeWithCompare
self.description = "corners" self.description = "corners"
end end
def create_node def do_create_node(cache)
args = [ self.input.create_node ] args = [ self.input.create_node(cache) ]
angle_delta = 1e-6 angle_delta = 1e-6
args &lt;&lt; (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0)) args &lt;&lt; (self.gt ? self.gt + angle_delta : (self.ge ? self.ge : -180.0))
args &lt;&lt; (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0)) args &lt;&lt; (self.lt ? self.lt : (self.le ? self.le - angle_delta : 180.0))
@ -905,7 +914,7 @@ class DRCOpNodeRelativeExtents &lt; DRCOpNode
end end
end end
def create_node def do_create_node(cache)
if !self.as_edges if !self.as_edges
RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy) RBA::CompoundRegionOperationNode::new_relative_extents_as_edges(self.input, self.fx1, self.fx2, self.fy1, self.fy2, self.dx, self.dy)
else else
@ -929,7 +938,7 @@ class DRCEngine
end end
def secondary(layer) def secondary(layer)
layer.requires_region layer.requires_region("secondary")
res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(layer.data)) res = DRCOpNode::new(self, RBA::CompoundRegionOperationNode::new_secondary(layer.data))
res.description = "secondary" res.description = "secondary"
return res return res