WIP: more consistent text handling

Texts are not only kept inside original layers, but
also inside deep layers. This enables using texts
from DRC.

However, texts in deep layers are kept as markers.
Mostly they are converted back to texts, but the
orientation will be lost.

The change eliminates the need to using Iterators
in DRC instead of original layers and use of
label layers in deep mode.

A drawback is the presence of marker shapes in
deep mode (unless polygon layers are created).
Also, text output to RDB is not supported from
deep layers currently.
This commit is contained in:
Matthias Koefferlein 2019-03-06 00:32:29 +01:00
parent c07d7e92d4
commit 8b29b30ff9
17 changed files with 851 additions and 297 deletions

View File

@ -461,13 +461,20 @@ unsigned int
DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans)
{
layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, trans));
if (l == m_layout_map.end ()) {
if (l == m_layout_map.end () || m_layouts[l->second] == 0) {
unsigned int layout_index = (unsigned int) m_layouts.size ();
unsigned int layout_index;
m_layouts.push_back (new LayoutHolder (trans));
if (l != m_layout_map.end ()) {
// reuse discarded entry
layout_index = l->second;
m_layouts[layout_index] = new LayoutHolder (trans);
} else {
layout_index = (unsigned int) m_layouts.size ();
m_layouts.push_back (new LayoutHolder (trans));
}
db::Layout &layout = m_layouts.back ()->layout;
db::Layout &layout = m_layouts[layout_index]->layout;
layout.hier_changed_event.add (this, &DeepShapeStore::invalidate_hier);
if (si.layout ()) {
layout.dbu (si.layout ()->dbu () / trans.mag ());
@ -577,6 +584,32 @@ DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator
return DeepLayer (this, layout_index, layer_index);
}
DeepLayer DeepShapeStore::create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe)
{
tl_assert (source.store () == this);
unsigned int from_layer_index = source.layer ();
db::Layout &ly = layout ();
unsigned int layer_index = ly.insert_layer ();
// Build the working hierarchy from the recursive shape iterator
tl::SelfTimer timer (tl::verbosity () >= 41, tl::to_string (tr ("Building working hierarchy")));
db::Box region = db::Box::world ();
db::ICplxTrans trans;
for (db::Layout::iterator c = ly.begin (); c != ly.end (); ++c) {
db::Shapes &into = c->shapes (layer_index);
const db::Shapes &from = c->shapes (from_layer_index);
for (db::Shapes::shape_iterator s = from.begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
pipe->push (*s, trans, region, 0, &into);
}
}
return DeepLayer (this, source.layout_index (), layer_index);
}
DeepLayer DeepShapeStore::create_edge_layer (const db::RecursiveShapeIterator &si, bool as_edges, const db::ICplxTrans &trans)
{
unsigned int layout_index = layout_for_iter (si, trans);
@ -725,6 +758,74 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
return cm->second;
}
namespace
{
class DeepShapeStoreToShapeTransformer
: public ShapesTransformer
{
public:
DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout)
: mp_layout (& layout)
{
// gets the text annotation property ID -
// this is how the texts are passed for annotating the net names
m_text_annot_name_id = std::pair<bool, db::property_names_id_type> (false, 0);
if (! dss.text_property_name ().is_nil ()) {
m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (dss.text_property_name ());
}
}
void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const
{
if (! m_text_annot_name_id.first) {
// fast shortcut
into.insert_transformed (from, trans, pm);
} else {
for (db::Shapes::shape_iterator i = from.begin (db::ShapeIterator::All); ! i.at_end (); ++i) {
bool is_text = false;
if (i->prop_id () > 0) {
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (i->prop_id ());
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) {
if (j->first == m_text_annot_name_id.second) {
db::Text text (j->second.to_string (), db::Trans (i->bbox ().center () - db::Point ()));
text.transform (trans);
if (into.layout ()) {
into.insert (db::TextRef (text, into.layout ()->shape_repository ()));
} else {
into.insert (text);
}
is_text = true;
}
}
}
if (! is_text) {
into.insert (*i, trans, pm);
}
}
}
}
private:
std::pair<bool, db::property_names_id_type> m_text_annot_name_id;
const db::Layout *mp_layout;
};
}
void
DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db::cell_index_type into_cell, unsigned int into_layer)
{
@ -750,8 +851,11 @@ DeepShapeStore::insert (const DeepLayer &deep_layer, db::Layout *into_layout, db
std::vector <db::cell_index_type> source_cells;
source_cells.push_back (*source_layout.begin_top_down());
// prepare a transformer to convert text-annotated markers back to texts (without transformation however)
DeepShapeStoreToShapeTransformer dsst (*this, source_layout);
// actually copy the shapes
db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm);
db::copy_shapes (*into_layout, source_layout, trans, source_cells, cm.table (), lm, &dsst);
}
void

View File

@ -334,6 +334,11 @@ public:
*/
DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ());
/**
* @brief Creates a deep layer as a copy from an existing one
*/
DeepLayer create_copy (const DeepLayer &source, HierarchyBuilderShapeReceiver *pipe);
/**
* @brief Gets the empty working layer
*

View File

@ -58,8 +58,6 @@ LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_i
if (dss->is_valid_layout_index (m_layout_index)) {
m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set<unsigned int> ());
}
init ();
}
LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu)
@ -131,9 +129,7 @@ db::Region *LayoutToNetlist::make_layer (const std::string &n)
si.shape_flags (db::ShapeIterator::Nothing);
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n);
}
register_layer (*region, n);
return region.release ();
}
@ -144,9 +140,7 @@ db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::st
si.shape_flags (db::ShapeIterator::All);
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n);
}
register_layer (*region, n);
return region.release ();
}
@ -157,9 +151,7 @@ db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const st
si.shape_flags (db::ShapeIterator::Texts);
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n);
}
register_layer (*region, n);
return region.release ();
}
@ -170,9 +162,7 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const
si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes);
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
if (! n.empty ()) {
register_layer (*region, n);
}
register_layer (*region, n);
return region.release ();
}

View File

@ -259,7 +259,8 @@ copy_or_propagate_shapes (db::Layout &target,
db::cell_index_type source_parent_cell_index,
unsigned int target_layer, unsigned int source_layer,
const std::set<db::cell_index_type> &all_cells_to_copy,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping)
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const ShapesTransformer *transformer)
{
const db::Cell &source_cell = source.cell (source_cell_index);
const db::Cell &source_parent_cell = source.cell (source_parent_cell_index);
@ -273,7 +274,7 @@ copy_or_propagate_shapes (db::Layout &target,
const db::CellInstArray &cell_inst = p->child_inst ().cell_inst ();
for (db::CellInstArray::iterator a = cell_inst.begin (); ! a.at_end (); ++a) {
db::ICplxTrans t = db::ICplxTrans (cell_inst.complex_trans (*a)) * propagate_trans;
copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping);
copy_or_propagate_shapes (target, source, trans, t, pm, source_cell_index, p->parent_cell_index (), target_layer, source_layer, all_cells_to_copy, cell_mapping, transformer);
}
}
@ -282,8 +283,7 @@ copy_or_propagate_shapes (db::Layout &target,
} else if (cm->second != DropCell) {
db::Cell &target_cell = target.cell (cm->second);
target_cell.shapes (target_layer).insert_transformed (source_cell.shapes (source_layer), trans * propagate_trans, pm);
transformer->insert_transformed (target_cell.shapes (target_layer), source_cell.shapes (source_layer), trans * propagate_trans, pm);
}
}
@ -294,7 +294,9 @@ copy_or_move_shapes (db::Layout &target,
const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping,
bool move)
const ShapesTransformer *transformer,
bool move
)
{
// collect all called cells and all top level cells
std::set<db::cell_index_type> all_top_level_cells;
@ -311,7 +313,7 @@ copy_or_move_shapes (db::Layout &target,
for (std::set<db::cell_index_type>::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) {
for (std::map<unsigned int, unsigned int>::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) {
++progress;
copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping);
copy_or_propagate_shapes (target, source, trans, db::ICplxTrans (), pm, *c, *c, lm->second, lm->first, all_cells_to_copy, cell_mapping, transformer);
if (move) {
source.cell (*c).shapes (lm->first).clear ();
}
@ -319,15 +321,33 @@ copy_or_move_shapes (db::Layout &target,
}
}
namespace
{
class StandardShapesTransformer
: public ShapesTransformer
{
public:
void insert_transformed (Shapes &into, const Shapes &from, const ICplxTrans &trans, PropertyMapper &pm) const
{
into.insert_transformed (from, trans, pm);
}
};
}
void
copy_shapes (db::Layout &target,
const db::Layout &source,
const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping)
const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer)
{
copy_or_move_shapes (target, const_cast<db::Layout &> (source), trans, source_cells, cell_mapping, layer_mapping, false);
StandardShapesTransformer st;
if (! transformer) {
transformer = &st;
}
copy_or_move_shapes (target, const_cast<db::Layout &> (source), trans, source_cells, cell_mapping, layer_mapping, transformer, false);
}
void
@ -336,9 +356,14 @@ move_shapes (db::Layout &target,
const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping)
const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer)
{
copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, true);
StandardShapesTransformer st;
if (! transformer) {
transformer = &st;
}
copy_or_move_shapes (target, source, trans, source_cells, cell_mapping, layer_mapping, transformer, true);
}
// ------------------------------------------------------------

View File

@ -140,6 +140,21 @@ merge_layouts (db::Layout &target, const db::Layout &source, const db::ICplxTran
const std::map<unsigned int, unsigned int> &layer_mapping,
std::map<db::cell_index_type, db::cell_index_type> *final_cell_mapping = 0);
/**
* @brief An interface for the shape inserter
*
* This interface is used by copy_shapes and move_shapes to insert
* a shape collection into a another one. By reimplementing this interface,
* more shape transformations can be provided.
*/
class DB_PUBLIC ShapesTransformer
{
public:
ShapesTransformer () { }
virtual ~ShapesTransformer () { }
virtual void insert_transformed (db::Shapes &into, const db::Shapes &from, const db::ICplxTrans &trans, db::PropertyMapper &pm) const = 0;
};
/**
* @brief Copy shapes from one layout to another
*
@ -155,7 +170,8 @@ copy_shapes (db::Layout &target,
const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping);
const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer = 0);
/**
* @brief Move shapes from one layout to another
@ -172,7 +188,8 @@ move_shapes (db::Layout &target,
const db::ICplxTrans &trans,
const std::vector<db::cell_index_type> &source_cells,
const std::map<db::cell_index_type, db::cell_index_type> &cell_mapping,
const std::map<unsigned int, unsigned int> &layer_mapping);
const std::map<unsigned int, unsigned int> &layer_mapping,
const ShapesTransformer *transformer = 0);
/**
* @brief Find an example cell instance from a child to a top cell

View File

@ -149,13 +149,13 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layo
// provide a substitute empty layer
layers.push_back (dss.empty_layer (layout_index).layer ());
} else {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: must be of deep region kind")), ld->name));
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): must be of deep region kind")), ld->name, name ()));
}
} else {
if (&dr->deep_layer ().layout () != &dss.layout (layout_index) || &dr->deep_layer ().initial_cell () != &dss.initial_cell (layout_index)) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: not originating from the same source")), ld->name));
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): not originating from the same source")), ld->name, name ()));
}
layers.push_back (dr->deep_layer ().layer ());

View File

@ -250,6 +250,20 @@ OriginalLayerRegion::init ()
m_merged_polygons_valid = false;
}
void
OriginalLayerRegion::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
{
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
// lock the layout against updates while inserting
db::LayoutLocker locker (layout);
for (db::RecursiveShapeIterator i = m_iter; !i.at_end (); ++i) {
tl::ident_map<db::properties_id_type> pm;
sh.insert (*i, i.trans (), pm);
}
}
void
OriginalLayerRegion::ensure_merged_polygons_valid () const
{
@ -284,165 +298,4 @@ OriginalLayerRegion::ensure_merged_polygons_valid () const
}
}
namespace
{
template <class Container>
struct dot_delivery
{
typedef Container container_type;
dot_delivery ()
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Edge (pt, pt));
}
};
template <class Container>
struct box_delivery
{
typedef Container container_type;
box_delivery (db::Coord enl)
: m_d (enl, enl)
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Box (pt - m_d, pt + m_d));
}
private:
db::Vector m_d;
};
template <class Iter, class Delivery>
static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const Delivery &delivery, typename Delivery::container_type *container)
{
tl::GlobPattern glob_pat;
bool all = false;
if (pattern) {
if (pat == "*") {
all = true;
} else {
glob_pat = tl::GlobPattern (pat);
}
}
for (Iter si = iter; ! si.at_end (); ++si) {
if (si->is_text () &&
(all || (pattern && glob_pat.match (si->text_string ())) || (!pattern && si->text_string () == pat))) {
db::Text t;
si->text (t);
t.transform (si.trans ());
delivery.insert (t.box ().center (), container);
}
}
}
template <class Delivery>
class text_shape_receiver
: public db::HierarchyBuilderShapeReceiver
{
public:
text_shape_receiver (const Delivery &delivery, const std::string &pat, bool pattern)
: m_delivery (delivery), m_glob_pat (), m_all (false), m_pattern (pattern), m_pat (pat)
{
if (pattern) {
if (m_pat == "*") {
m_all = true;
} else {
m_glob_pat = tl::GlobPattern (pat);
}
}
}
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &region, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
{
if (shape.is_text () &&
(m_all || (m_pattern && m_glob_pat.match (shape.text_string ())) || (!m_pattern && shape.text_string () == m_pat))) {
db::Point pt = shape.bbox ().center ();
if (! complex_region) {
if (region.contains (pt)) {
m_delivery.insert (pt.transformed (trans), target);
}
} else {
if (! complex_region->begin_overlapping (db::Box (pt, pt), db::box_convert<db::Box> ()).at_end ()) {
m_delivery.insert (pt.transformed (trans), target);
}
}
}
}
virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
private:
Delivery m_delivery;
tl::GlobPattern m_glob_pat;
bool m_all;
bool m_pattern;
std::string m_pat;
};
}
db::EdgesDelegate *
OriginalLayerRegion::texts_as_dots (const std::string &pat, bool pattern) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
std::auto_ptr<db::FlatEdges> res (new db::FlatEdges ());
res->set_merged_semantics (false);
fill_texts (iter, pat, pattern, dot_delivery<db::FlatEdges> (), res.get ());
return res.release ();
}
db::EdgesDelegate *
OriginalLayerRegion::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
text_shape_receiver<dot_delivery<db::Shapes> > pipe = text_shape_receiver<dot_delivery<db::Shapes> > (dot_delivery<db::Shapes> (), pat, pattern);
return new db::DeepEdges (store.create_custom_layer (iter, &pipe));
}
db::RegionDelegate *
OriginalLayerRegion::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
std::auto_ptr<db::FlatRegion> res (new db::FlatRegion ());
res->set_merged_semantics (false);
fill_texts (iter, pat, pattern, box_delivery<db::FlatRegion> (enl), res.get ());
return res.release ();
}
db::RegionDelegate *
OriginalLayerRegion::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const
{
db::RecursiveShapeIterator iter (m_iter);
iter.shape_flags (db::ShapeIterator::Texts);
text_shape_receiver<box_delivery<db::Shapes> > pipe = text_shape_receiver<box_delivery<db::Shapes> > (box_delivery<db::Shapes> (enl), pat, pattern);
return new db::DeepRegion (store.create_custom_layer (iter, &pipe));
}
}

View File

@ -68,10 +68,7 @@ public:
virtual bool equals (const Region &other) const;
virtual bool less (const Region &other) const;
db::EdgesDelegate *texts_as_dots (const std::string &pat, bool pattern) const;
db::EdgesDelegate *texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const;
db::RegionDelegate *texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const;
db::RegionDelegate *texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const;
virtual void insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const;
protected:
virtual void merged_semantics_changed ();

View File

@ -26,7 +26,10 @@
#include "dbEmptyRegion.h"
#include "dbFlatRegion.h"
#include "dbDeepRegion.h"
#include "dbDeepEdges.h"
#include "dbFlatEdges.h"
#include "dbPolygonTools.h"
#include "tlGlobPattern.h"
namespace db
{
@ -398,6 +401,291 @@ Region::hulls () const
return Region (processed (HullExtractionProcessor ()));
}
namespace
{
template <class Container>
struct dot_delivery
{
typedef Container container_type;
dot_delivery ()
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Edge (pt, pt));
}
};
template <class Container>
struct box_delivery
{
typedef Container container_type;
box_delivery (db::Coord enl)
: m_d (enl, enl)
{
// .. nothing yet ..
}
void insert (const db::Point &pt, Container *container) const
{
container->insert (db::Box (pt - m_d, pt + m_d));
}
private:
db::Vector m_d;
};
template <class Iter, class Delivery>
static void fill_texts (const Iter &iter, const std::string &pat, bool pattern, const Delivery &delivery, typename Delivery::container_type *container, const db::ICplxTrans &trans, const db::DeepRegion *org_deep)
{
std::pair<bool, db::property_names_id_type> text_annot_name_id;
const db::Layout *layout = 0;
if (org_deep) {
layout = &org_deep->deep_layer ().layout ();
const db::DeepShapeStore *store = org_deep->deep_layer ().store ();
if (! store->text_property_name ().is_nil ()) {
text_annot_name_id = layout->properties_repository ().get_id_of_name (store->text_property_name ());
}
}
tl::GlobPattern glob_pat;
bool all = false;
if (pattern) {
if (pat == "*") {
all = true;
} else {
glob_pat = tl::GlobPattern (pat);
}
}
for (Iter si = iter; ! si.at_end (); ++si) {
bool is_text = false;
std::string text_string;
if (si->is_text ()) {
// a raw text
is_text = true;
text_string = si->text_string ();
} else if (layout && text_annot_name_id.first && si->prop_id () > 0) {
// a text marker
const db::PropertiesRepository::properties_set &ps = layout->properties_repository ().properties (si->prop_id ());
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) {
if (j->first == text_annot_name_id.second) {
text_string = j->second.to_string ();
is_text = true;
}
}
}
if (is_text &&
(all || (pattern && glob_pat.match (text_string)) || (!pattern && text_string == pat))) {
delivery.insert (si.trans () * (trans * si->bbox ().center ()), container);
}
}
}
template <class Delivery>
class text_shape_receiver
: public db::HierarchyBuilderShapeReceiver
{
public:
text_shape_receiver (const Delivery &delivery, const std::string &pat, bool pattern, const db::DeepRegion *org_deep)
: m_delivery (delivery), m_glob_pat (), m_all (false), m_pattern (pattern), m_pat (pat), m_text_annot_name_id (false, 0), mp_layout (0)
{
if (org_deep) {
mp_layout = & org_deep->deep_layer ().layout ();
const db::DeepShapeStore *store = org_deep->deep_layer ().store ();
if (! store->text_property_name ().is_nil ()) {
m_text_annot_name_id = mp_layout->properties_repository ().get_id_of_name (store->text_property_name ());
}
}
if (pattern) {
if (m_pat == "*") {
m_all = true;
} else {
m_glob_pat = tl::GlobPattern (pat);
}
}
}
virtual void push (const db::Shape &shape, const db::ICplxTrans &trans, const db::Box &region, const db::RecursiveShapeReceiver::box_tree_type *complex_region, db::Shapes *target)
{
bool is_text = false;
std::string text_string;
if (shape.is_text ()) {
// a raw text
is_text = true;
text_string = shape.text_string ();
} else if (mp_layout && m_text_annot_name_id.first && shape.prop_id () > 0) {
// a text marker
const db::PropertiesRepository::properties_set &ps = mp_layout->properties_repository ().properties (shape.prop_id ());
for (db::PropertiesRepository::properties_set::const_iterator j = ps.begin (); j != ps.end () && ! is_text; ++j) {
if (j->first == m_text_annot_name_id.second) {
text_string = j->second.to_string ();
is_text = true;
}
}
}
if (is_text &&
(m_all || (m_pattern && m_glob_pat.match (text_string)) || (!m_pattern && text_string == m_pat))) {
db::Point pt = shape.bbox ().center ();
if (! complex_region) {
if (region.contains (pt)) {
m_delivery.insert (pt.transformed (trans), target);
}
} else {
if (! complex_region->begin_overlapping (db::Box (pt, pt), db::box_convert<db::Box> ()).at_end ()) {
m_delivery.insert (pt.transformed (trans), target);
}
}
}
}
virtual void push (const db::Box &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
virtual void push (const db::Polygon &, const db::ICplxTrans &, const db::Box &, const db::RecursiveShapeReceiver::box_tree_type *, db::Shapes *) { }
private:
Delivery m_delivery;
tl::GlobPattern m_glob_pat;
bool m_all;
bool m_pattern;
std::string m_pat;
std::pair<bool, db::property_names_id_type> m_text_annot_name_id;
const db::Layout *mp_layout;
};
}
Edges
Region::texts_as_dots (const std::string &pat, bool pattern) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
if (dr) {
return texts_as_dots (pat, pattern, const_cast<db::DeepShapeStore &> (*dr->deep_layer ().store ()));
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
std::auto_ptr<db::FlatEdges> res (new db::FlatEdges ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, dot_delivery<db::FlatEdges> (), res.get (), si.second, dr);
return Edges (res.release ());
}
Edges
Region::texts_as_dots (const std::string &pat, bool pattern, db::DeepShapeStore &store) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
if (! si.first.layout ()) {
// flat fallback if the source isn't a deep or original layer
std::auto_ptr<db::FlatEdges> res (new db::FlatEdges ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, dot_delivery<db::FlatEdges> (), res.get (), si.second, dr);
return Edges (res.release ());
}
text_shape_receiver<dot_delivery<db::Shapes> > pipe = text_shape_receiver<dot_delivery<db::Shapes> > (dot_delivery<db::Shapes> (), pat, pattern, dr);
if (dr && dr->deep_layer ().store () == &store) {
return Edges (new db::DeepEdges (store.create_copy (dr->deep_layer (), &pipe)));
} else {
return Edges (new db::DeepEdges (store.create_custom_layer (si.first, &pipe, si.second)));
}
}
Region
Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
if (dr) {
return texts_as_boxes (pat, pattern, enl, const_cast<db::DeepShapeStore &> (*dr->deep_layer ().store ()));
}
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
std::auto_ptr<db::FlatRegion> res (new db::FlatRegion ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, box_delivery<db::FlatRegion> (enl), res.get (), si.second, dr);
return Region (res.release ());
}
Region
Region::texts_as_boxes (const std::string &pat, bool pattern, db::Coord enl, db::DeepShapeStore &store) const
{
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (delegate ());
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> si = begin_iter ();
if (! dr) {
// some optimization
si.first.shape_flags (db::ShapeIterator::Texts);
}
if (! si.first.layout ()) {
// flat fallback if the source isn't a deep or original layer
std::auto_ptr<db::FlatRegion> res (new db::FlatRegion ());
res->set_merged_semantics (false);
fill_texts (si.first, pat, pattern, box_delivery<db::FlatRegion> (enl), res.get (), si.second, dr);
return Region (res.release ());
}
text_shape_receiver<box_delivery<db::Shapes> > pipe = text_shape_receiver<box_delivery<db::Shapes> > (box_delivery<db::Shapes> (enl), pat, pattern, dr);
if (dr && dr->deep_layer ().store () == &store) {
return Region (new db::DeepRegion (store.create_copy (dr->deep_layer (), &pipe)));
} else {
return Region (new db::DeepRegion (store.create_custom_layer (si.first, &pipe, si.second)));
}
}
}
namespace tl

View File

@ -1516,6 +1516,40 @@ public:
return mp_delegate->insert_into (layout, into_cell, into_layer);
}
/**
* @brief Delivers texts as dots (degenerated edges)
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text..
*/
db::Edges texts_as_dots (const std::string &pat, bool as_pattern) const;
/**
* @brief Delivers texts as dots (degenerated edges) in a deep edge collection
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text..
*/
db::Edges texts_as_dots (const std::string &pat, bool as_pattern, db::DeepShapeStore &store) const;
/**
* @brief Delivers texts as boxes
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.
* "enl" is the half size of the box (the box is 2*enl wide and 2*enl high).
*/
db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl) const;
/**
* @brief Delivers texts as boxes in a deep region
*
* "pat" is a text selector. If "as_pattern" is true, this pattern will be
* treated as a glob pattern. Otherwise, the text is taken if "pat" is equal to the text.
* "enl" is the half size of the box (the box is 2*enl wide and 2*enl high).
*/
db::Region texts_as_boxes (const std::string &pat, bool as_pattern, db::Coord enl, db::DeepShapeStore &store) const;
private:
friend class Edges;
friend class EdgePairs;

View File

@ -392,12 +392,12 @@ static bool is_deep (const db::Edges *e)
static db::Edges *new_texts_as_dots1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern)
{
return new db::Edges (db::OriginalLayerRegion (si, false).texts_as_dots (pat, pattern));
return new db::Edges (db::Region (si).texts_as_dots (pat, pattern));
}
static db::Edges *new_texts_as_dots2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern)
{
return new db::Edges (db::OriginalLayerRegion (si, false).texts_as_dots (pat, pattern, dss));
return new db::Edges (db::Region (si).texts_as_dots (pat, pattern, dss));
}
static size_t id (const db::Edges *e)

View File

@ -80,41 +80,32 @@ static db::Region *new_shapes (const db::Shapes &s)
static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl)
{
return new db::Region (db::OriginalLayerRegion (si, false).texts_as_boxes (pat, pattern, enl));
return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl));
}
static db::Region *new_texts_as_boxes2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl)
{
return new db::Region (db::OriginalLayerRegion (si, false).texts_as_boxes (pat, pattern, enl, dss));
}
static const db::OriginalLayerRegion *org_layer (const db::Region *r)
{
const db::OriginalLayerRegion *org_layer = dynamic_cast<const db::OriginalLayerRegion *> (r->delegate ());
if (! org_layer) {
throw tl::Exception (tl::to_string (tr ("Texts can only be identified on an original layer")));
}
return org_layer;
return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl, dss));
}
static db::Edges *texts_as_dots1 (const db::Region *r, const std::string &pat, bool pattern)
{
return new db::Edges (org_layer (r)->texts_as_dots (pat, pattern));
return new db::Edges (r->texts_as_dots (pat, pattern));
}
static db::Edges *texts_as_dots2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern)
{
return new db::Edges (org_layer (r)->texts_as_dots (pat, pattern, dss));
return new db::Edges (r->texts_as_dots (pat, pattern, dss));
}
static db::Region *texts_as_boxes1 (const db::Region *r, const std::string &pat, bool pattern, db::Coord enl)
{
return new db::Region (org_layer (r)->texts_as_boxes (pat, pattern, enl));
return new db::Region (r->texts_as_boxes (pat, pattern, enl));
}
static db::Region *texts_as_boxes2 (const db::Region *r, db::DeepShapeStore &dss, const std::string &pat, bool pattern, db::Coord enl)
{
return new db::Region (org_layer (r)->texts_as_boxes (pat, pattern, enl, dss));
return new db::Region (r->texts_as_boxes (pat, pattern, enl, dss));
}
static db::Edges corners_to_dots (const db::Region *r, double angle_start, double angle_end)
@ -142,6 +133,11 @@ static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICpl
return new db::Region (si, trans);
}
static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count)
{
return new db::Region (si, dss, trans, area_ratio, max_vertex_count);
}
static std::string to_string0 (const db::Region *r)
{
return r->to_string ();
@ -653,9 +649,8 @@ Class<db::Region> decl_Region ("db", "Region",
"\n"
"This constructor has been introduced in version 0.25."
) +
constructor ("new", &new_si,
constructor ("new", &new_si, gsi::arg ("shape_iterator"),
"@brief Constructor from a hierarchical shape set\n"
"@args shape_iterator\n"
"\n"
"This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n"
"Text objects and edges are not inserted, because they cannot be converted to polygons.\n"
@ -668,9 +663,8 @@ Class<db::Region> decl_Region ("db", "Region",
"r = RBA::Region::new(layout.begin_shapes(cell, layer))\n"
"@/code\n"
) +
constructor ("new", &new_si2,
constructor ("new", &new_si2, gsi::arg ("shape_iterator"), gsi::arg ("trans"),
"@brief Constructor from a hierarchical shape set with a transformation\n"
"@args shape_iterator, trans\n"
"\n"
"This constructor creates a region from the shapes delivered by the given recursive shape iterator.\n"
"Text objects and edges are not inserted, because they cannot be converted to polygons.\n"
@ -698,7 +692,24 @@ Class<db::Region> decl_Region ("db", "Region",
"@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n"
"@param deep_shape_store The hierarchical heap (see there)\n"
"@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n"
"@param"
"\n"
"This method has been introduced in version 0.26.\n"
) +
constructor ("new", &new_sid2, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("trans"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)),
"@brief Constructor for a deep region from a hierarchical shape set\n"
"\n"
"This constructor creates a hierarchical region. Use a \\DeepShapeStore object to "
"supply the hierarchical heap. See \\DeepShapeStore for more details.\n"
"\n"
"'area_ratio' and 'max_vertex' supply two optimization parameters which control how "
"big polygons are split to reduce the region's polygon complexity.\n"
"\n"
"The transformation is useful to scale to a specific database unit for example.\n"
"\n"
"@param shape_iterator The recursive shape iterator which delivers the hierarchy to take\n"
"@param deep_shape_store The hierarchical heap (see there)\n"
"@param area_ratio The maximum ratio of bounding box to polygon area before polygons are split\n"
"@param trans The transformation to apply when storing the layout data\n"
"\n"
"This method has been introduced in version 0.26.\n"
) +

View File

@ -1198,19 +1198,19 @@ TEST(23_Texts)
unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1));
unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1));
db::Region r6boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100, dss));
db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100, dss));
db::Region r6dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20);
db::Region r8boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100, dss));
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20);
db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100, dss));
db::Region r8dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20);
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20);
db::Region rf6boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100));
db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_boxes ("*", true, 100));
db::Region rf6dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20);
db::Region rf8boxes (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100));
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6)).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20);
db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_boxes ("VDD", false, 100));
db::Region rf8dots;
db::Edges (db::OriginalLayerRegion (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20);
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8)).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20);
{
db::Layout target;
@ -1230,6 +1230,59 @@ TEST(23_Texts)
}
}
TEST(24_TextsFromDeep)
{
db::Layout ly;
{
std::string fn (tl::testsrc ());
fn += "/testdata/algo/deep_region_l1.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
db::DeepShapeStore dss;
dss.set_text_enlargement (1);
dss.set_text_property_name (tl::Variant ("textstring"));
unsigned int l6 = ly.get_layer (db::LayerProperties (6, 1));
unsigned int l8 = ly.get_layer (db::LayerProperties (8, 1));
db::Region r6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100, dss));
db::Region r6dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true, dss)).extended (r6dots, 20, 20, 20, 20);
db::Region r8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100, dss));
db::Region r8dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true, dss)).extended (r8dots, 20, 20, 20, 20);
db::Region rf6boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_boxes ("*", true, 100));
db::Region rf6dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l6), dss).texts_as_dots ("*", true)).extended (rf6dots, 20, 20, 20, 20);
db::Region rf8boxes (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_boxes ("VDD", false, 100));
db::Region rf8dots;
db::Edges (db::Region (db::RecursiveShapeIterator (ly, top_cell, l8), dss).texts_as_dots ("V*", true)).extended (rf8dots, 20, 20, 20, 20);
{
db::Layout target;
unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (10, 0)), r6boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r6dots);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), rf6boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (13, 0)), rf6dots);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (20, 0)), r8boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (21, 0)), r8dots);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (22, 0)), rf8boxes);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (23, 0)), rf8dots);
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au24.gds");
}
}
TEST(100_Integration)
{
db::Layout ly;

View File

@ -3488,10 +3488,21 @@ CODE
# @li @tt METAL (17/0) @/tt: A layer named "METAL" or layer 17, datatype 0 (for GDS, which does
# not have names)@/li
# @/ul
#
# Layers created with "input" contain both texts and polygons. There is a subtle
# difference between flat and deep mode: in flat mode, texts are not visible in polygon
# operations. In deep mode, texts appear as small 2x2 DBU rectangles. In flat mode,
# some operations such as clipping are not fully supported for texts. Also, texts will
# vanish in most polygon operations such as booleans etc.
#
# Texts can alway be selected by using the \texts method.
#
# If you don't want to see texts, use \polygons to create an input layer with polygon data
# only. If you only want to see texts, use \labels to create an input layer with texts only.
def input(*args)
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, false))
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll))
end
# %DRC%
@ -3501,26 +3512,45 @@ CODE
# @synopsis source.labels(layer, datatype)
# @synopsis source.labels(layer_into)
# @synopsis source.labels(filter, ...)
# Creates a layer with the labels from the given layer of the source.
# The layer can be specified by layer and optionally datatype, by a RBA::LayerInfo
# object or by a sequence of filters.
# Filters are expressions describing ranges
# of layers and/or datatype numbers or layer names. Multiple filters
# can be given and all layers matching at least one of these filter
# expressions are joined to render the label collection. See "input" for
# more details about the input layer specification.
#
# Label layers currently can only be passed to an output layer.
# Processing of labels is not supported. See "texts" for a way to filter
# texts and use the text locations in geometrical operations.
#
# @code
# labels(1, 0).output(100, 0)
# @/code
# Creates a layer with the labels from the given layer of the source.
#
# This method is identical to \input, but takes only texts from the given input
# layer.
def labels(*args)
layers = parse_input_layers(*args)
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, true))
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::STexts))
end
# %DRC%
# @name polygons
# @brief Gets the polygon shapes (or shapes that can be converted polygons) from an input layer
# @synopsis source.polygons(layer)
# @synopsis source.polygons(layer, datatype)
# @synopsis source.polygons(layer_into)
# @synopsis source.polygons(filter, ...)
#
# Creates a layer with the polygon shapes from the given layer of the source.
# With "polygon shapes" we mean all kind of shapes that can be converted to polygons.
# Those are boxes, paths and real polygons.
#
# This method is identical to \input with respect to the options supported.
def polygons(*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))
end
# %DRC%
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchy of the layout
# @synopsis make_layer
# This method delivers a new empty original layer.
def make_layer
layers = []
DRCLayer::new(@engine, @engine._cmd(@engine, :_input, @layout_var, @cell.cell_index, layers, @sel, @box, @clip, @overlapping, RBA::Shapes::SAll))
end
# %DRC%
@ -3689,6 +3719,73 @@ CODE
@connections &lt;&lt; [ a, b ].collect { |l| l.data.data_id }
modified
end
# %DRC%
# @name connect_global
# @brief Connects a layer with a global net
# @synopsis connect_global(l, name)
# Connects the shapes from the given layer l to a global net with the given name.
# Global nets are common to all cells. Global nets automatically connect to parent
# cells throughs implied pins. An example is the substrate (bulk) net which connects
# to shapes belonging to tie-down diodes.
def connect_global(l, name)
l.is_a?(DRC::DRCLayer) || raise("Layer argument of Netter#connect_global must be a layer")
l.requires_region("Netter#connect_global (layer argument)")
@layers[l.data.data_id] = l.data
@global_connections &lt;&lt; [ l.data.data_id, name.to_s ]
end
# %DRC%
# @name extract_devices
# @brief Extracts devices based on the given extractor class, name and device layer selection
# @synopsis extract_devices(extractor, layer_hash)
# Runs the device extraction for given device extractor class.
#
# The device extractor is either an instance of one of the predefined extractor
# classes (e.g. RBA::DeviceExtractorMOS4Transistor) or a custom class. It provides the
# algorithms for deriving the device parameters from the device geometry. It needs
# several device recognition layers which are passed in the layer hash.
#
# Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance
# of device extractor. The device extractor beside the algorithm and specific
# extraction settings defines the name of the device to be built.
#
# The layer hash is a map of device type specific functional names (key) and
# polygon layers (value). Here is an example:
#
# @code
# deep
#
# nwell = input(1, 0)
# active = input(2, 0)
# poly = input(3, 0)
# bulk = make_layer # renders an empty layer used for putting the terminals on
#
# nactive = active - nwell # active area of NMOS
# nsd = nactive - poly # source/drain area
# gate = nactive &amp; poly # gate area
#
# mos4_ex = RBA::DeviceExtractorMOS4Transistor::new("NMOS4")
# extract_devices(mos4_ex, { :SD => nsd, :G => gate, :P => poly, :W => bulk })
# @/code
def extract_devices(devex, layer_selection)
devex.is_a?(RBA::DeviceExtractorBase) || raise("First argument of Netter#extract_devices must be a device extractor instance")
layer_selection.is_a?(Hash) || raise("Second argument of Netter#extract_devices must be a hash")
ls = {}
layer_selection.each do |n,l|
l.requires_region("Netter#extract_devices (#{n} layer)")
@layers[l.data.data_id] = l.data
ls[n.to_s] = l.data
end
@devices_to_extract &lt;&lt; [ devex, ls ]
modified
end
# %DRC%
# @name clear_connections
@ -3697,7 +3794,9 @@ CODE
# See \connect for more details.
def clear_connections
@devices_to_extract = []
@connections = []
@global_connections = []
@layers = {}
modified
end
@ -3803,6 +3902,18 @@ CODE
DRC::DRCLayer::new(@engine, @engine._cmd(@l2n, :antenna_check, gate.data, metal.data, ratio, dl))
end
# %DRC%
# @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object
# @synopsis l2n_data
# The RBA::LayoutToNetlist object provides access to the internal details of
# the netter object.
def l2n_data
@l2n || make_l2n
@l2n
end
def _finish
clear_connections
@ -3830,8 +3941,14 @@ CODE
end
@layers.each { |id,l| @l2n.register(l, "l" + id.to_s) }
@devices_to_extract.each do |devex,ls|
@engine._cmd(@l2n, :extract_devices, devex, ls)
end
@layers.each { |id,l| @l2n.connect(l) }
@connections.each { |a,b| @l2n.connect(@layers[a], @layers[b]) }
@global_connections.each { |l,n| @l2n.connect_global(@layers[l], n) }
# run extraction in a timed environment
@engine._cmd(@l2n, :extract_netlist)
@ -4200,12 +4317,33 @@ CODE
@tt = n.to_i
end
# %DRC%
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchical scheme selected
# @synopsis make_layer
# The intention of this method is to provide an empty polygon layer based on the
# hierarchical scheme selected. This will create a new layer with the hierarchy
# of the current layout in deep mode and a flat layer in flat mode.
# This method is similar to \polygon_layer, but the latter does not create
# a hierarchical layer. Hence the layer created by \make_layer is suitable
# for use in device extraction for example, while the one
# delivered by \polygon_layer is not.
#
# On the other hand, a layer created by the \make_layer method is not intended to be
# filled with \Layer#insert.
def make_layer
layout.make_layer
end
# %DRC%
# @name polygon_layer
# @brief Creates an empty polygon layer
# @synopsis polygon_layer
# The intention of that method is to create an empty layer which can be
# filled with polygon-like objects using \Layer#insert.
# A similar method which creates a hierarchical layer in deep mode is
# \make_layer. This other layer is better suited for use with device extraction.
def polygon_layer
DRCLayer::new(self, RBA::Region::new)
@ -4621,12 +4759,24 @@ CODE
# @name input
# @brief Fetches the shapes from the specified input from the default source
# @synopsis input(args)
# See \Source#input for a description of that function.
# See \Source#input for a description of that function. This method will fetch
# polygons and labels. See \polygons and \labels for more specific versions of
# this method.
def input(*args)
layout.input(*args)
end
# %DRC%
# @name polygons
# @brief Fetches the polygons (or shapes that can be converted to polygons) from the specified input from the default source
# @synopsis polygons(args)
# See \Source#polygons for a description of that function.
def polygons(*args)
layout.polygons(*args)
end
# %DRC%
# @name labels
# @brief Gets the labels (text) from an original layer
@ -4754,6 +4904,12 @@ CODE
# @synopsis connect(a, b)
# See \Netter#connect for a description of that function.
# %DRC%
# @name connect_global
# @brief Specifies a connection to a global net
# @synopsis connect_global(l, name)
# See \Netter#connect_global for a description of that function.
# %DRC%
# @name clear_connections
# @brief Clears all connections stored so far
@ -4766,7 +4922,19 @@ CODE
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ])
# See \Netter#antenna_check for a description of that function
%w(connect clear_connections antenna_check).each do |f|
# %DRC%
# @name l2n_data
# @brief Gets the internal RBA::LayoutToNetlist object for the default \Netter
# @synopsis l2n_data
# See \Netter#l2n_data for a description of that function
# %DRC%
# @name extract_devices
# @brief Extracts devices for a given device extractor and device layer selection
# @synopsis extract_devices(extractor, layer_hash)
# See \Netter#extract_devices for a description of that function
%w(connect connect_global clear_connections antenna_check l2n_data extract_devices).each do |f|
eval &lt;&lt;"CODE"
def #{f}(*args)
_netter.#{f}(*args)
@ -5033,10 +5201,12 @@ CODE
end
end
def _input(layout, cell_index, layers, sel, box, clip, overlapping, labels_only)
def _input(layout, cell_index, layers, sel, box, clip, overlapping, shape_flags)
if layers.empty?
if layers.empty? &amp;&amp; ! @deep
r = RBA::Region::new
else
if box
@ -5044,9 +5214,7 @@ CODE
else
iter = RBA::RecursiveShapeIterator::new(layout, layout.cell(cell_index), layers)
end
if labels_only
iter.shape_flags = RBA::Shapes::STexts
end
iter.shape_flags = shape_flags
sel.each do |s|
if s == "-"
@ -5064,31 +5232,17 @@ CODE
end
end
if labels_only
# for labels layers, the iterator is the data (no region)
r = iter
sf = layout.dbu / self.dbu
if @deep
@dss ||= RBA::DeepShapeStore::new
r = RBA::Region::new(iter, @dss, RBA::ICplxTrans::new(sf.to_f))
else
r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f))
end
sf = layout.dbu / self.dbu
if (sf - 1.0).abs &gt; 1e-6
if @deep
raise("DBU scaling (" + ("%.12g" % layout.dbu) + " to " + ("%.12g" % self.dbu) + ") is not supported in deep mode currently")
end
r = RBA::Region::new(iter, RBA::ICplxTrans::new(sf.to_f))
elsif @deep
@dss ||= RBA::DeepShapeStore::new
r = RBA::Region::new(iter, @dss)
else
r = RBA::Region::new(iter)
end
# clip if a box is specified
if box &amp;&amp; clip
r &amp;= RBA::Region::new(box)
end
# clip if a box is specified
if box &amp;&amp; clip
r &amp;= RBA::Region::new(box)
end
end
@ -5112,11 +5266,7 @@ CODE
cat = @output_rdb.create_category(args[0].to_s)
args[1] &amp;&amp; cat.description = args[1]
if data.is_a?(RBA::RecursiveShapeIterator)
cat.scan_shapes(data)
else
cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data)
end
cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data)
else
@ -5173,24 +5323,11 @@ CODE
@used_output_layers[li] = true
end
if data.respond_to?(:is_deep?) &amp;&amp; data.is_deep?
# insert the data into the output layer
if data.is_a?(RBA::EdgePairs)
data.insert_into_as_polygons(output, output_cell.cell_index, tmp, 1)
else
data.insert_into(output, output_cell.cell_index, tmp)
end
# insert the data into the output layer
if data.is_a?(RBA::EdgePairs)
data.insert_into_as_polygons(output, output_cell.cell_index, tmp, 1)
else
# insert the data into the output layer
if data.is_a?(RBA::EdgePairs)
output_cell.shapes(tmp).insert_as_polygons(data, 1)
else
output_cell.shapes(tmp).insert(data)
end
data.insert_into(output, output_cell.cell_index, tmp)
end
# make the temp layer the output layer

View File

@ -302,3 +302,43 @@ TEST(7_AntennaWithDiodes)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
TEST(8_FlatTextsAndPolygons)
{
std::string rs = tl::testsrc ();
rs += "/testdata/drc/drcSimpleTests_8.drc";
std::string input = tl::testsrc ();
input += "/testdata/drc/texts.gds";
std::string au = tl::testsrc ();
au += "/testdata/drc/drcSimpleTests_au8.oas";
std::string output = this->tmp_file ("tmp.gds");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (_this, layout, au, db::NoNormalization);
}

BIN
testdata/algo/deep_region_au24.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/texts.gds vendored Normal file

Binary file not shown.