diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc
index 2671686ac..6df7a711f 100644
--- a/src/db/db/dbOriginalLayerRegion.cc
+++ b/src/db/db/dbOriginalLayerRegion.cc
@@ -379,6 +379,12 @@ OriginalLayerRegion::init ()
void
OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
{
+ // explicitly update the layout if the source is equal to the target
+ // (needed because we lock the layout below and no update would happen)
+ if (layout == m_iter.layout ()) {
+ layout->update ();
+ }
+
db::Shapes &sh = layout->cell (into_cell).shapes (into_layer);
// NOTE: if the source (r) is from the same layout than the shapes live in, we better
diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc
index 9ff2f70b3..edf3363cd 100644
--- a/src/db/db/gsiDeclDbLayout.cc
+++ b/src/db/db/gsiDeclDbLayout.cc
@@ -41,6 +41,7 @@
#include "dbCellMapping.h"
#include "dbTechnology.h"
#include "tlStream.h"
+#include "tlGlobPattern.h"
namespace gsi
{
@@ -708,6 +709,22 @@ static db::Cell *cell_from_name (db::Layout *ly, const std::string &name)
}
}
+static std::vector cells_from_name (db::Layout *layout, const std::string &filter)
+{
+ tl::GlobPattern gp (filter);
+
+ std::vector result;
+ db::Layout::top_down_iterator td = layout->begin_top_down ();
+ while (td != layout->end_top_down ()) {
+ if (gp.match (layout->cell_name (*td))) {
+ result.push_back (&layout->cell (*td));
+ }
+ ++td;
+ }
+
+ return result;
+}
+
static std::vector top_cells (db::Layout *layout)
{
std::vector tc;
@@ -1488,6 +1505,14 @@ Class decl_Layout ("db", "Layout",
"\n"
"@return The number of cells (the maximum cell index)\n"
) +
+ gsi::method_ext ("cells", &cells_from_name, gsi::arg ("name_filter"),
+ "@brief Gets the cell objects for a given name filter\n"
+ "\n"
+ "@param name_filter The cell name filter (glob pattern)\n"
+ "@return A list of \\Cell object of the cells matching the pattern\n"
+ "\n"
+ "This method has been introduced in version 0.27.3.\n"
+ ) +
gsi::method_ext ("cell", &cell_from_name, gsi::arg ("name"),
"@brief Gets a cell object from the cell name\n"
"\n"
diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb
index 6359bff21..f5fc0e9cf 100644
--- a/src/drc/drc/built-in-macros/_drc_engine.rb
+++ b/src/drc/drc/built-in-macros/_drc_engine.rb
@@ -1484,13 +1484,14 @@ module DRC
# %DRC%
# @name extent
- # @brief Creates a new layer with the bounding box of the default source
+ # @brief Creates a new layer with the bounding box of the default source or cell bounding boxes
# @synopsis extent
+ # @synopsis extent(cell_filter)
# See \Source#extent for a description of that function.
- def extent
+ def extent(cell_filter = nil)
self._context("extent") do
- layout.extent
+ layout.extent(cell_filter)
end
end
@@ -1615,6 +1616,8 @@ CODE
# # shapes now will be taken from the given rectangle and clipped to it
# l1 = input(1, 0)
# @/code
+ #
+ # To remove the clip condition, call "clip" without any arguments.
def clip(*args)
self._context("clip") do
@@ -2638,16 +2641,6 @@ CODE
end
end
- private
-
- def _make_string(v)
- if v.class.respond_to?(:from_s)
- v.class.to_s + "::from_s(" + v.to_s.inspect + ")"
- else
- v.inspect
- end
- end
-
def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags, global_trans, cls)
if layers.empty? && ! @deep
@@ -2714,6 +2707,16 @@ CODE
end
+ private
+
+ def _make_string(v)
+ if v.class.respond_to?(:from_s)
+ v.class.to_s + "::from_s(" + v.to_s.inspect + ")"
+ else
+ v.inspect
+ end
+ end
+
def _layout(name)
@layout_sources[name].layout
end
diff --git a/src/drc/drc/built-in-macros/_drc_source.rb b/src/drc/drc/built-in-macros/_drc_source.rb
index 538ecf968..600630949 100644
--- a/src/drc/drc/built-in-macros/_drc_source.rb
+++ b/src/drc/drc/built-in-macros/_drc_source.rb
@@ -101,7 +101,9 @@ module DRC
@engine._context(method) do
box = nil
- if args.size == 1
+ if args.size == 0
+ # unclip
+ elsif args.size == 1
box = args[0]
box.is_a?(RBA::DBox) || raise("Method requires a box specification")
elsif args.size == 2
@@ -112,7 +114,7 @@ module DRC
else
raise("Invalid number of arguments (1, 2 or 4 expected)")
end
- @box = RBA::Box::from_dbox(box * (1.0 / @layout.dbu))
+ @box = box && RBA::Box::from_dbox(box * (1.0 / @layout.dbu))
self
@@ -311,24 +313,55 @@ CODE
# %DRC%
# @name extent
- # @brief Returns a layer with the bounding box of the selected layout
+ # @brief Returns a layer with the bounding box of the selected layout or cells
# @synopsis source.extent
+ # @synopsis source.extent(cell_filter)
+ #
+ # Without an argument, the extent method returns a layer with the bounding box
+ # of the top cell. With a cell filter argument, the method returns a layer
+ # with the bounding boxes of the selected cells. The cell filter is a glob
+ # pattern.
+ #
# The extent function is useful to invert a layer:
#
# @code
# inverse_1 = extent.sized(100.0) - input(1, 0)
# @/code
+ #
+ # The following example returns the bounding boxes of all cells whose
+ # names start with "A":
+ #
+ # @code
+ # a_cells = extent("A*")
+ # @/code
- def extent
+ def extent(cell_filter = nil)
+
@engine._context("extent") do
- layer = input
- if @box
- layer.insert(RBA::DBox::from_ibox(@box) * @layout.dbu)
- else
- layer.insert((RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu).transformed(@global_trans))
+
+ if cell_filter
+ cell_filter.is_a?(String) || raise("Invalid cell filter argument - must be a string")
end
+
+ if cell_filter
+ tmp = @layout_var.insert_layer(RBA::LayerInfo::new)
+ @tmp_layers << tmp
+ @layout_var.cells(cell_filter).each do |cell|
+ cell.shapes(tmp).insert(cell.bbox)
+ end
+ layer = DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, [tmp], @sel, @box, @clip, @overlapping, RBA::Shapes::SAll, @global_trans, RBA::Region))
+ else
+ layer = input
+ layer.insert((RBA::DBox::from_ibox(@cell.bbox) * @layout.dbu).transformed(@global_trans))
+ if @box
+ layer.data &= RBA::Region::new(@box)
+ end
+ end
+
layer
+
end
+
end
# %DRC%
diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc
index ad98a0644..5bcba5fa3 100644
--- a/src/drc/unit_tests/drcSimpleTests.cc
+++ b/src/drc/unit_tests/drcSimpleTests.cc
@@ -1303,3 +1303,12 @@ TEST(51_epInternalAngle)
run_test (_this, "51", false);
}
+TEST(52_cellWiseExtent)
+{
+ run_test (_this, "52", false);
+}
+
+TEST(53_cellWiseExtentWithClip)
+{
+ run_test (_this, "53", false);
+}
diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml
index 917ada706..96c622861 100644
--- a/src/lay/lay/doc/about/drc_ref_global.xml
+++ b/src/lay/lay/doc/about/drc_ref_global.xml
@@ -306,6 +306,8 @@ clip(0.mm, 0.mm, 0.5.mm, 0.6.mm)
# shapes now will be taken from the given rectangle and clipped to it
l1 = input(1, 0)
+
+To remove the clip condition, call "clip" without any arguments.
"connect" - Specifies a connection between two layers
@@ -619,11 +621,12 @@ actual edges from the first input (see separation for
Similar to log, but the message is printed formatted as an error
-"extent" - Creates a new layer with the bounding box of the default source
+"extent" - Creates a new layer with the bounding box of the default source or cell bounding boxes
Usage:
- extent
+- extent(cell_filter)
See Source#extent for a description of that function.
diff --git a/src/lay/lay/doc/about/drc_ref_source.xml b/src/lay/lay/doc/about/drc_ref_source.xml
index 05525f41e..20a46bee9 100644
--- a/src/lay/lay/doc/about/drc_ref_source.xml
+++ b/src/lay/lay/doc/about/drc_ref_source.xml
@@ -96,18 +96,31 @@ Use the global version of "edges" without a source object to address the default
This method has been introduced in version 0.27.
-"extent" - Returns a layer with the bounding box of the selected layout
+"extent" - Returns a layer with the bounding box of the selected layout or cells
Usage:
- source.extent
+- source.extent(cell_filter)
+Without an argument, the extent method returns a layer with the bounding box
+of the top cell. With a cell filter argument, the method returns a layer
+with the bounding boxes of the selected cells. The cell filter is a glob
+pattern.
+
The extent function is useful to invert a layer:
inverse_1 = extent.sized(100.0) - input(1, 0)
+
+The following example returns the bounding boxes of all cells whose
+names start with "A":
+
+
+a_cells = extent("A*")
+
"global_transform" - Gets or sets a global transformation
diff --git a/testdata/drc/drcSimpleTests_52.drc b/testdata/drc/drcSimpleTests_52.drc
new file mode 100644
index 000000000..91d043f52
--- /dev/null
+++ b/testdata/drc/drcSimpleTests_52.drc
@@ -0,0 +1,14 @@
+
+source $drc_test_source
+target $drc_test_target
+
+extent.output(2000)
+extent("PMOS*").output(2001)
+extent("NMOS*").output(2002)
+
+deep
+
+extent.output(2100)
+extent("PMOS*").output(2101)
+extent("NMOS*").output(2102)
+
diff --git a/testdata/drc/drcSimpleTests_52.gds b/testdata/drc/drcSimpleTests_52.gds
new file mode 100644
index 000000000..e498a3028
Binary files /dev/null and b/testdata/drc/drcSimpleTests_52.gds differ
diff --git a/testdata/drc/drcSimpleTests_53.drc b/testdata/drc/drcSimpleTests_53.drc
new file mode 100644
index 000000000..0e84e9d2f
--- /dev/null
+++ b/testdata/drc/drcSimpleTests_53.drc
@@ -0,0 +1,16 @@
+
+source $drc_test_source
+target $drc_test_target
+
+clip(7.0, -1.0, 15.0, 10.0)
+
+extent.output(2000)
+extent("PMOS*").output(2001)
+extent("NMOS*").output(2002)
+
+deep
+
+extent.output(2100)
+extent("PMOS*").output(2101)
+extent("NMOS*").output(2102)
+
diff --git a/testdata/drc/drcSimpleTests_53.gds b/testdata/drc/drcSimpleTests_53.gds
new file mode 100644
index 000000000..e498a3028
Binary files /dev/null and b/testdata/drc/drcSimpleTests_53.gds differ
diff --git a/testdata/drc/drcSimpleTests_au52.gds b/testdata/drc/drcSimpleTests_au52.gds
new file mode 100644
index 000000000..dfdea7c1b
Binary files /dev/null and b/testdata/drc/drcSimpleTests_au52.gds differ
diff --git a/testdata/drc/drcSimpleTests_au53.gds b/testdata/drc/drcSimpleTests_au53.gds
new file mode 100644
index 000000000..9d1fa94d4
Binary files /dev/null and b/testdata/drc/drcSimpleTests_au53.gds differ
diff --git a/testdata/ruby/dbLayoutTest.rb b/testdata/ruby/dbLayoutTest.rb
index db993a61f..4d08991c3 100644
--- a/testdata/ruby/dbLayoutTest.rb
+++ b/testdata/ruby/dbLayoutTest.rb
@@ -79,6 +79,7 @@ class DBLayout_TestClass < TestBase
assert_equal( ly.cell_name(ci), "new_cell" )
assert_equal( ly.cell_by_name("new_cell"), ci )
+ assert_equal( ly.cells("A*"), [] )
assert_equal( ly.cell("new_cell").name, "new_cell" )
assert_equal( ly.cell("x").inspect, "nil" )
@@ -1175,6 +1176,23 @@ class DBLayout_TestClass < TestBase
end
+ # Layout#cells
+ def test_15
+
+ g = RBA::Layout::new
+ c1 = g.create_cell("B1")
+ c2 = g.create_cell("B2")
+ c0 = g.create_cell("A")
+ c0.insert(RBA::CellInstArray::new(c1.cell_index, RBA::Trans::new))
+ c0.insert(RBA::CellInstArray::new(c2.cell_index, RBA::Trans::new))
+
+ assert_equal(g.cells("B*").collect(&:name).join(","), "B1,B2")
+ assert_equal(g.cells("*").collect(&:name).join(","), "A,B1,B2")
+ assert_equal(g.cells("A").collect(&:name).join(","), "A")
+ assert_equal(g.cells("X").collect(&:name).join(","), "")
+
+ end
+
end
load("test_epilogue.rb")