Rename some functions, DRC binding for 'evaluate_nets'

This commit is contained in:
Matthias Koefferlein 2025-08-02 23:06:22 +02:00
parent 61aa08cd64
commit 73681755ed
9 changed files with 149 additions and 18 deletions

View File

@ -216,7 +216,7 @@ static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &pol
return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes, texts);
}
static db::Region measure_net (db::LayoutToNetlist *l2n, const db::Region &primary, const std::map<std::string, const db::Region *> &secondary, const std::string &expression, const tl::Variant &variables)
static db::Region evaluate_nets (db::LayoutToNetlist *l2n, const db::Region &primary, const std::map<std::string, const db::Region *> &secondary, const std::string &expression, const tl::Variant &variables)
{
if (! variables.is_array ()) {
throw tl::Exception (tl::to_string (tr ("'variables' argument needs to a hash")));
@ -1224,7 +1224,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"This variant has been introduced in version 0.26.6.\n"
) +
gsi::method_ext ("measure_net", &measure_net, gsi::arg ("primary"), gsi::arg ("secondary"), gsi::arg ("expression"), gsi::arg ("variables", std::map<std::string, tl::Variant> (), "{}"),
gsi::method_ext ("evaluate_nets", &evaluate_nets, gsi::arg ("primary"), gsi::arg ("secondary"), gsi::arg ("expression"), gsi::arg ("variables", std::map<std::string, tl::Variant> (), "{}"),
"@brief Runs a generic net measurement function\n"
"\n"
"This method accepts some primary layer, a number of secondary layers with names and an expression.\n"

View File

@ -2437,6 +2437,12 @@ CODE
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ])
# See \Netter#antenna_check for a description of that function.
# %DRC%
# @name evaluate_nets
# @brief Evaluates expressions on nets
# @synopsis evaluate_nets(primary_layer, secondary_layers, expression [, variables])
# See \Netter#evaluate_nets for a description of that function.
# %DRC%
# @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object for the default \Netter
@ -2489,6 +2495,7 @@ CODE
%w(
antenna_check
evaluate_nets
clear_connections
connect
connect_global

View File

@ -5762,36 +5762,36 @@ END
other.is_a?(DRCLayer) || raise("Argument needs to be a DRC layer")
end
def requires_region
self.data.is_a?(RBA::Region) || raise("Requires a polygon layer")
def requires_region(name = nil)
self.data.is_a?(RBA::Region) || raise(name ? "#{name} requires a polygon layer" : "Requires a polygon layer")
end
def requires_texts_or_region
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise("Requires a polygon or text layer")
def requires_texts_or_region(name = nil)
self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise(name ? "#{name} requires a polygon or text layer" : "Requires a polygon or text layer")
end
def requires_texts
self.data.is_a?(RBA::Texts) || raise("Requires a text layer")
def requires_texts(name = nil)
self.data.is_a?(RBA::Texts) || raise(name ? "#{name} requires a text layer" : "Requires a text layer")
end
def requires_edge_pairs
self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge pair layer")
def requires_edge_pairs(name = nil)
self.data.is_a?(RBA::EdgePairs) || raise(name ? "#{name} requires an edge pair layer" : "Requires an edge pair layer")
end
def requires_edges
self.data.is_a?(RBA::Edges) || raise("Requires an edge layer")
def requires_edges(name = nil)
self.data.is_a?(RBA::Edges) || raise(name ? "#{name} requires an edge layer" : "Requires an edge layer")
end
def requires_edges_or_edge_pairs
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise("Requires an edge or edge pair layer")
def requires_edges_or_edge_pairs(name = nil)
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::EdgePairs) || raise(name ? "#{name} requires an edge or edge pair layer" : "Requires an edge or edge pair layer")
end
def requires_edges_or_region
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise("Requires an edge or polygon layer")
def requires_edges_or_region(name = nil)
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || raise(name ? "#{name} requires an edge or polygon layer" : "Requires an edge or polygon layer")
end
def requires_edges_texts_or_region
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise("Requires an edge, text or polygon layer")
def requires_edges_texts_or_region(name = nil)
self.data.is_a?(RBA::Edges) || self.data.is_a?(RBA::Region) || self.data.is_a?(RBA::Texts) || raise(name ? "#{name} requires an edge, text or polygon layer" : "Requires an edge, text or polygon layer")
end
def requires_same_type(other)

View File

@ -769,6 +769,90 @@ module DRC
end
# %DRC%
# @name evaluate_nets
# @brief Evaluates net properties and annotates shapes from a given layer on the nets
# @synopsis evaluate_nets(primary_layer, secondary_layers, expression [, variables])
#
# The function takes a primary layer and a number of secondary layers, each of
# them given a variable name.
# It visits each net and evaluates the given expression on the net.
# The expression needs to be written in KLayout expression notations.
#
# By default, the shapes of the primary layer are copied to the output.
#
# Using the "put" function inside the expression, properties can be
# attached to the output shapes. The properties can be computed using
# a number of net attributes - area and perimeter, but also the RBA::Net
# object representing the net. This allows implementing a more elaborate
# antenna check for example.
#
# Also, the expression can choose to drop shapes and not copy then to
# the output by calling the "skip" function with a "true" argument.
#
# Arbitrary values can be passed as variables, which removes the need
# to encode variable values into the expression.
#
# The following functions are available
#
# @ul
# @li "net" - the RBA::Net object of the current net
# @li "skip(flag)" - if called with a 'true' argument, the primary layer's shapes are not copied for this net
# @li "put(name, value)" - places the value as a property with name 'name' (this must be a string) on the output shapes
# @li "area" - the combined area of the primary layer's shapes on the net in square micrometer units
# @li "area(symbol)" - the combined area of the secondary layer's shapes on the net in square micrometer units
# @li "perimeter" - the perimeter of the primary layer's shapes on the net in micrometer units
# @li "perimeter(symbol)" - the perimeter of the secondary layer's shapes on the net in micrometer units
# @/ul
#
# Here, 'symbol' is the name given to the secondary layer in the secondary layer
# dictionary.
#
# The following example computes the area ratio of metal vs. gate area and
# attaches the value on a property with name 'AR' to the shapes of a copy of the
# 'gate' layer:
#
# @code
# gate = ... # a layer with the gate shapes
# metal = ... # a layer with metal shapes
#
# # NOTE: 'MET' is the name we are going to use in the expression
# antenna_errors = evaluate_nets(gate, { "MET" => metal }, "put('AR',area(MET)/area)")
# @/code
#
# The following example computes the area ratio of metal vs. gate area and
# outputs the gate shapes of all nets whose metal to gate area ratio is bigger than
# 500. The area ratio is output to a property with name 'AR':
#
# @code
# gate = ... # a layer with the gate shapes
# metal = ... # a layer with metal shapes
#
# variables = { "thr" => 500.0 }
# expression = "var ar=area(MET)/area; put('AR',ar); skip(ar<thr)"
#
# antenna_errors = evaluate_nets(gate, { "MET" => metal }, expression, variables)
# @/code
def evaluate_nets(primary, secondary, expression, variables = {})
primary.is_a?(DRC::DRCLayer) || raise("First argument must be a layer")
primary.requires_region("'primary'")
secondary_data = {}
secondary.is_a?(Hash) || raise("Second argument must be a hash of names and layers")
secondary.each do |k,v|
v.is_a?(DRC::DRCLayer) || raise("Second argument must be a hash of names and polygon")
v.requires_region("Secondary '#{k}'")
secondary_data[k.to_s] = v.data
end
expression.is_a?(String) || raise("'expression' argument must be a string")
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :evaluate_nets, primary.data, secondary_data, expression, variables))
end
# %DRC%
# @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object

View File

@ -2013,3 +2013,14 @@ TEST(141d_merge_properties)
{
run_test (_this, "141", true);
}
TEST(142_evaluate_nets)
{
run_test (_this, "142", false);
}
TEST(142d_evaluate_nets)
{
run_test (_this, "142", true);
}

29
testdata/drc/drcSimpleTests_142.drc vendored Normal file
View File

@ -0,0 +1,29 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
l4 = input(4, 0)
l5 = input(5, 0)
connect(l1, l2)
connect(l2, l3)
connect(l3, l4)
connect(l4, l5)
l1_measured = evaluate_nets(l1, { "l2" => l2, "l3" => l3, "l4" => l4, "l5" => l5}, "put(5, area(l5)); put(0, area); put(100, area(l5)/area)")
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
l4.output(4, 0)
l5.output(5, 0)
l1_measured.output(100, 0)

BIN
testdata/drc/drcSimpleTests_142.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au142.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au142d.gds vendored Normal file

Binary file not shown.