Merge pull request #1396 from KLayout/wip2

Wip2
This commit is contained in:
Matthias Köfferlein 2023-06-10 10:29:30 +02:00 committed by GitHub
commit 01e4e46fea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1801 additions and 360 deletions

View File

@ -805,11 +805,13 @@ for pi in dbpi_dirs:
mod_name = "_" + os.path.split(os.path.split(pi)[-2])[-1] + "_dbpi"
pi_sources = glob.glob(os.path.join(pi, "*.cc"))
pi_sources += glob.glob(os.path.join(pi, "contrib", "*.cc"))
pi_ext = Library(
config.root + ".db_plugins." + mod_name,
define_macros=config.macros() + [("MAKE_DB_PLUGIN_LIBRARY", 1)],
include_dirs=[
pi,
os.path.join("src", "plugins", "common"),
_db_path,
_tl_path,

View File

@ -41,6 +41,7 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
std::string infile_a, infile_b;
std::string top_a, top_b;
bool silent = false;
bool ignore_duplicates = false;
bool no_text_orientation = true;
bool no_text_details = true;
bool no_properties = false;
@ -106,6 +107,10 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
<< tl::arg ("--expand-arrays", &flatten_array_insts, "Expands array instances before compare",
"With this option, arrays are equivalent single instances are treated identical."
)
<< tl::arg ("-1|--ignore-duplicates", &ignore_duplicates, "Ignore duplicate instances and shapes",
"With this option, duplicate instances or shapes are ignored and duplication "
"does not count as a difference."
)
<< tl::arg ("-l|--layer-details", &dont_summarize_missing_layers, "Prints details about differences for missing layers",
"With this option, missing layers are treated as \"empty\" and details about differences to "
"other, non-empty layers are printed. Essentially the content of the non-empty counterpart "
@ -155,6 +160,9 @@ BD_PUBLIC int strmcmp (int argc, char *argv[])
if (silent) {
flags |= db::layout_diff::f_silent;
}
if (ignore_duplicates) {
flags |= db::layout_diff::f_ignore_duplicates;
}
if (no_text_orientation) {
flags |= db::layout_diff::f_no_text_orientation;
}

View File

@ -538,7 +538,7 @@ public:
* @brief Default ctor
*/
box_scanner2 (bool report_progress = false, const std::string &progress_desc = std::string ())
: m_fill_factor (2), m_scanner_thr (100),
: m_fill_factor (2), m_scanner_thr (100), m_scanner_thr1 (10),
m_report_progress (report_progress), m_progress_desc (progress_desc)
{
// .. nothing yet ..
@ -564,6 +564,26 @@ public:
return m_scanner_thr;
}
/**
* @brief Sets the scanner threshold per class
*
* This value determines for how many elements in one class the implementation switches to the scanner
* implementation instead of the plain element-by-element interaction test.
* The default value is 10.
*/
void set_scanner_threshold1 (size_t n)
{
m_scanner_thr1 = n;
}
/**
* @brief Gets the scanner threshold per class
*/
size_t scanner_threshold1 () const
{
return m_scanner_thr1;
}
/**
* @brief Sets the fill factor
*
@ -667,7 +687,7 @@ private:
container_type1 m_pp1;
container_type2 m_pp2;
double m_fill_factor;
size_t m_scanner_thr;
size_t m_scanner_thr, m_scanner_thr1;
bool m_report_progress;
std::string m_progress_desc;
@ -732,7 +752,7 @@ private:
rec.finish2 (i->first, i->second);
}
} else if (m_pp1.size () + m_pp2.size () <= m_scanner_thr) {
} else if (m_pp1.size () + m_pp2.size () <= m_scanner_thr || m_pp2.size () <= m_scanner_thr1 || m_pp1.size () <= m_scanner_thr1) {
// below m_scanner_thr elements use the brute force approach which is faster in that case

View File

@ -187,6 +187,17 @@ Cell::clear (unsigned int index)
}
}
void
Cell::clear (unsigned int index, unsigned int types)
{
shapes_map::iterator s = m_shapes_map.find(index);
if (s != m_shapes_map.end() && ! s->second.empty ()) {
mp_layout->invalidate_bboxes (index); // HINT: must come before the change is done!
s->second.clear (types);
m_bbox_needs_update = true;
}
}
Cell::shapes_type &
Cell::shapes (unsigned int index)
{
@ -344,6 +355,20 @@ Cell::copy (unsigned int src, unsigned int dest)
}
}
void
Cell::copy (unsigned int src, unsigned int dest, unsigned int types)
{
if (src != dest) {
shapes (dest).insert (shapes (src), types);
} else {
// When duplicating the layer, first create a copy to avoid problems with non-stable containers
// Hint: using the assignment and not the copy ctor does not copy the db::Manager association.
db::Shapes shape_copy;
shape_copy.insert (shapes (src), types);
shapes (dest).insert (shape_copy);
}
}
void
Cell::move (unsigned int src, unsigned int dest)
{
@ -353,6 +378,15 @@ Cell::move (unsigned int src, unsigned int dest)
}
}
void
Cell::move (unsigned int src, unsigned int dest, unsigned int types)
{
if (src != dest) {
copy (src, dest, types);
clear (src, types);
}
}
void
Cell::swap (unsigned int i1, unsigned int i2)
{

View File

@ -182,6 +182,13 @@ public:
*/
void copy (unsigned int src, unsigned int dest);
/**
* @brief Copy the shapes from layer src to dest (only shapes from given classes)
*
* The target layer is not overwritten. Instead, the shapes are added to the target layer's shapes.
*/
void copy (unsigned int src, unsigned int dest, unsigned int types);
/**
* @brief Move the shapes from layer src to dest
*
@ -189,6 +196,13 @@ public:
*/
void move (unsigned int src, unsigned int dest);
/**
* @brief Move the shapes from layer src to dest (only shapes from given classes)
*
* The target layer is not overwritten. Instead, the shapes are added to the target layer's shapes.
*/
void move (unsigned int src, unsigned int dest, unsigned int types);
/**
* @brief Swap the layers given
*/
@ -199,7 +213,12 @@ public:
*/
void clear (unsigned int index);
/**
/**
* @brief Clear the shapes on the given layer (only the shapes from the given classes)
*/
void clear (unsigned int index, unsigned int types);
/**
* @brief Erase a cell instance given by a instance proxy
*
* Erasing a cell instance will destroy the sorting order and invalidate

View File

@ -962,10 +962,31 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
// Implement XOR as (A-B)+(B-A) - only this implementation
// is compatible with the local processor scheme
DeepLayer n1 (and_or_not_with (other_deep, false, property_constraint));
DeepLayer n2 (other_deep->and_or_not_with (this, false, property_constraint));
// Prepare a version of "other_deep" that is mapped into the hierarchy space
// of "this"
std::unique_ptr<DeepRegion> other_deep_mapped;
if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) {
// shallow copy for reconfiguration (progress etc.)
other_deep_mapped.reset (new DeepRegion (other_deep->deep_layer ()));
} else {
// deep copy with mapped hierarchy
other_deep_mapped.reset (new DeepRegion (deep_layer ().derived ()));
other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ());
}
other_deep_mapped->set_strict_handling (strict_handling ());
other_deep_mapped->set_base_verbosity (base_verbosity ());
if (report_progress ()) {
other_deep_mapped->enable_progress (progress_desc () + tl::to_string (tr (" - reverse part")));
} else {
other_deep_mapped->disable_progress ();
}
DeepLayer n1 (and_or_not_with (other_deep_mapped.get (), false, property_constraint));
DeepLayer n2 (other_deep_mapped->and_or_not_with (this, false, property_constraint));
n1.add_from (n2);
return new DeepRegion (n1);
}

View File

@ -64,6 +64,11 @@ cronology::events::event_collection<event_compute_results, event_compute_local_c
namespace db
{
// Heuristic parameter to control the recursion of the cell-to-cell intruder
// detection: do not recurse if the intruder cell's bounding box is smaller
// than the overlap box times this factor.
const double area_ratio_for_recursion = 3.0;
// ---------------------------------------------------------------------------------------------
// Shape reference translator
@ -370,6 +375,32 @@ db::Box safe_box_enlarged (const db::Box &box, db::Coord dx, db::Coord dy)
}
}
// ---------------------------------------------------------------------------------------------
// Debugging utility: dump the cell contexts
template <class TS, class TI, class TR>
static void dump_cell_contexts (local_processor_contexts<TS, TI, TR> &contexts, const db::Layout *subject_layout, const db::Layout *intruder_layout)
{
for (auto cc = contexts.begin (); cc != contexts.end (); ++cc) {
tl::info << "Cell " << subject_layout->cell_name (cc->first->cell_index ()) << ":";
int i = 0;
for (auto c = cc->second.begin (); c != cc->second.end (); ++c) {
tl::info << " Context #" << ++i;
tl::info << " Instances:";
for (auto i = c->first.first.begin (); i != c->first.first.end (); ++i) {
const db::CellInstArray &ci = *i;
tl::info << " " << intruder_layout->cell_name (ci.object ().cell_index ()) << " @ " << ci.complex_trans (*ci.begin ()).to_string () << " (" << ci.size () << ")";
}
tl::info << " Shapes:";
for (auto i = c->first.second.begin (); i != c->first.second.end (); ++i) {
for (auto s = i->second.begin (); s != i->second.end (); ++s) {
tl::info << " " << intruder_layout->get_properties (i->first).to_string () << ": " << s->to_string ();
}
}
}
}
}
// ---------------------------------------------------------------------------------------------
// LocalProcessorCellContext implementation
@ -1208,7 +1239,8 @@ private:
void
collect_instance_interactions (const db::CellInstArray *inst1, const db::CellInstArray *inst2)
{
// TODO: this algorithm is not in particular effective for identical arrays
// TODO: this algorithm is not in particular effective for identical arrays or for arrays
// vs. single instances
const db::Cell &cell1 = mp_subject_layout->cell (inst1->object ().cell_index ());
const db::Cell &cell2 = mp_intruder_layout->cell (inst2->object ().cell_index ());
@ -1247,7 +1279,7 @@ private:
db::Box ibox2 = tn2 * cell2.bbox (m_intruder_layer).enlarged (db::Vector (m_dist, m_dist));
db::Box cbox = ibox1 & ibox2;
if (! cbox.empty () && cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1))) {
if (! cbox.empty () && (cbox == ibox1 || cell1.has_shapes_touching (m_subject_layer, safe_box_enlarged (tni1 * cbox, -1, -1)))) {
db::ICplxTrans tn21 = tni1 * tn2;
@ -1282,9 +1314,11 @@ private:
db::ICplxTrans tni2 = tn21.inverted () * tni1;
db::Box tbox2 = safe_box_enlarged (tni2 * cbox, -1, -1);
if (! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
// do not recurse further if we're overlapping with shapes from the intruder
// or the intruder cell is not much bigger than the region of interest (cbox)
if (intruder_cell.bbox (m_intruder_layer).area () < area_ratio_for_recursion * cbox.area ()
|| ! intruder_cell.shapes (m_intruder_layer).begin_touching (tbox2, ShapeIterator::All).at_end ()) {
// we're overlapping with shapes from the intruder - do not recursive further
interactions.push_back (std::make_pair (intruder_cell.cell_index (), tn21));
return;
@ -1782,6 +1816,11 @@ template <class TS, class TI, class TR>
void
local_processor<TS, TI, TR>::compute_results (local_processor_contexts<TS, TI, TR> &contexts, const local_operation<TS, TI, TR> *op, const std::vector<unsigned int> &output_layers) const
{
#if 0
// debugging
dump_cell_contexts (contexts, mp_subject_layout, mp_intruder_layout ? mp_intruder_layout : mp_subject_layout);
#endif
tl::SelfTimer timer (tl::verbosity () > m_base_verbosity + 10, tl::to_string (tr ("Computing results for ")) + description (op));
// avoids updates while we work on the layout

View File

@ -1971,7 +1971,19 @@ Layout::move_layer (unsigned int src, unsigned int dest)
}
}
void
void
Layout::move_layer (unsigned int src, unsigned int dest, unsigned int flags)
{
tl_assert (m_layers.layer_state (src) != LayoutLayers::Free);
tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free);
// move the shapes
for (iterator c = begin (); c != end (); ++c) {
c->move (src, dest, flags);
}
}
void
Layout::copy_layer (unsigned int src, unsigned int dest)
{
tl_assert (m_layers.layer_state (src) != LayoutLayers::Free);
@ -1983,7 +1995,19 @@ Layout::copy_layer (unsigned int src, unsigned int dest)
}
}
void
void
Layout::copy_layer (unsigned int src, unsigned int dest, unsigned int flags)
{
tl_assert (m_layers.layer_state (src) != LayoutLayers::Free);
tl_assert (m_layers.layer_state (dest) != LayoutLayers::Free);
// copy the shapes
for (iterator c = begin (); c != end (); ++c) {
c->copy (src, dest, flags);
}
}
void
Layout::clear_layer (unsigned int n)
{
tl_assert (m_layers.layer_state (n) != LayoutLayers::Free);
@ -1994,7 +2018,18 @@ Layout::clear_layer (unsigned int n)
}
}
void
void
Layout::clear_layer (unsigned int n, unsigned int flags)
{
tl_assert (m_layers.layer_state (n) != LayoutLayers::Free);
// clear the shapes
for (iterator c = begin (); c != end (); ++c) {
c->clear (n, flags);
}
}
void
Layout::delete_layer (unsigned int n)
{
tl_assert (m_layers.layer_state (n) != LayoutLayers::Free);

View File

@ -1377,7 +1377,15 @@ public:
*/
void move_layer (unsigned int src, unsigned int dest);
/**
/**
* @brief Move a layer (selected shape types only)
*
* Move a layer from the source to the target. The target is not cleared before, so that this method
* merges shapes from the source with the target layer. The source layer is empty after that operation.
*/
void move_layer (unsigned int src, unsigned int dest, unsigned int flags);
/**
* @brief Copy a layer
*
* Copy a layer from the source to the target. The target is not cleared before, so that this method
@ -1385,14 +1393,29 @@ public:
*/
void copy_layer (unsigned int src, unsigned int dest);
/**
/**
* @brief Copy a layer (selected shape types only)
*
* Copy a layer from the source to the target. The target is not cleared before, so that this method
* merges shapes from the source with the target layer.
*/
void copy_layer (unsigned int src, unsigned int dest, unsigned int flags);
/**
* @brief Clear a layer
*
* Clears the layer: removes all shapes.
*/
void clear_layer (unsigned int n);
/**
/**
* @brief Clear a layer (selected shapes only)
*
* Clears the layer: removes the shapes of the type given the flags (ShapeIterator::shapes_type)
*/
void clear_layer (unsigned int n, unsigned int flags);
/**
* @brief Delete a layer
*
* This does free the shapes of the cells and remembers the

View File

@ -69,8 +69,10 @@ collect_cells (const db::Layout &l, const db::Cell *top, std::map <std::string,
}
static void
collect_insts_of_unmapped_cells (const db::Layout & /*l*/, const db::Cell *cell, unsigned int /*flags*/, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &insts)
collect_insts_of_unmapped_cells (const db::Layout & /*l*/, const db::Cell *cell, unsigned int /*flags*/, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &insts, bool no_duplicates)
{
size_t n_before = insts.size ();
for (db::Cell::const_iterator i = cell->begin (); !i.at_end (); ++i) {
std::map <db::cell_index_type, db::cell_index_type>::const_iterator ccii = cci.find (i->cell_index ());
@ -81,6 +83,13 @@ collect_insts_of_unmapped_cells (const db::Layout & /*l*/, const db::Cell *cell,
}
}
if (no_duplicates) {
std::sort (insts.begin () + n_before, insts.end ());
insts.erase (std::unique (insts.begin () + n_before, insts.end ()), insts.end ());
}
}
static void
@ -102,7 +111,7 @@ rewrite_instances_to (std::vector <db::CellInstArrayWithProperties> &insts, unsi
}
static void
collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flags, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &insts, PropertyMapper &pn)
collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flags, const std::map <db::cell_index_type, db::cell_index_type> &cci, std::vector <db::CellInstArrayWithProperties> &insts, PropertyMapper &pn, bool no_duplicates)
{
insts.clear ();
@ -148,6 +157,10 @@ collect_insts (const db::Layout & /*l*/, const db::Cell *cell, unsigned int flag
}
std::sort (insts.begin (), insts.end ());
if (no_duplicates) {
insts.erase (std::unique (insts.begin (), insts.end ()), insts.end ());
}
}
/**
@ -178,10 +191,10 @@ int compare_seq (I b1, I e1, I b2, I e2, Op op)
/**
* @brief Reduces two vectors to the common objects as determined by the compare operator
* If the iterate parameter is true, the reduction is repeated until no more reduction can be
* achieved. This is useful with tolerances since the sorted is not strict in that case.
* achieved. This is useful with tolerances since the sorting is not strict in that case.
*/
template <class X, class Op>
void reduce (std::vector<X> &a, std::vector<X> &b, Op op, bool iterate)
void reduce (std::vector<X> &a, std::vector<X> &b, Op op, bool iterate, bool no_duplicates)
{
do {
@ -196,12 +209,29 @@ void reduce (std::vector<X> &a, std::vector<X> &b, Op op, bool iterate)
while (ra != a.end () && rb != b.end ()) {
if (op (*ra, *rb)) {
*wa++ = *ra++;
typename std::vector<X>::const_iterator r = ra++;
*wa = *r;
while (no_duplicates && ra != a.end () && !op (*ra, *r) && !op(*r, *ra)) {
++ra;
}
++wa;
} else if (op (*rb, *ra)) {
*wb++ = *rb++;
typename std::vector<X>::const_iterator r = rb++;
*wb = *r;
while (no_duplicates && rb != b.end () && !op (*rb, *r) && !op(*r, *rb)) {
++rb;
}
++wb;
} else {
++ra;
++rb;
typename std::vector<X>::const_iterator r;
r = ra++;
while (no_duplicates && ra != a.end () && !op (*ra, *r) && !op(*r, *ra)) {
++ra;
}
r = rb++;
while (no_duplicates && rb != b.end () && !op (*rb, *r) && !op(*r, *rb)) {
++rb;
}
}
}
@ -211,14 +241,22 @@ void reduce (std::vector<X> &a, std::vector<X> &b, Op op, bool iterate)
if (ra != wa) {
while (ra != a.end ()) {
*wa++ = *ra++;
typename std::vector<X>::const_iterator r = ra++;
*wa++ = *r;
while (no_duplicates && ra != a.end () && !op (*ra, *r) && !op(*r, *ra)) {
++ra;
}
}
a.erase (wa, a.end ());
}
if (rb != wb) {
while (rb != b.end ()) {
*wb++ = *rb++;
typename std::vector<X>::const_iterator r = rb++;
*wb++ = *r;
while (no_duplicates && rb != b.end () && !op (*rb, *r) && !op(*r, *rb)) {
++rb;
}
}
b.erase (wb, b.end ());
}
@ -405,7 +443,7 @@ struct PolygonCompareOpWithTolerance
m_eb.push_back (*e);
}
reduce (m_ea, m_eb, EdgeCompareOpWithTolerance (m_tolerance), m_tolerance > 0);
reduce (m_ea, m_eb, EdgeCompareOpWithTolerance (m_tolerance), m_tolerance > 0, false);
return compare_seq (m_ea.begin (), m_ea.end (), m_eb.begin (), m_eb.end (), EdgeCompareOpWithTolerance (m_tolerance)) < 0;
}
@ -665,6 +703,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
}
bool verbose = (flags & layout_diff::f_verbose);
bool no_duplicates = (flags & layout_diff::f_ignore_duplicates);
db::Layout n, na, nb;
na.properties_repository () = a.properties_repository ();
@ -897,20 +936,20 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
r.bbox_differs (cell_a->bbox (), cell_b->bbox ());
}
collect_insts (a, cell_a, flags, common_cell_indices_a, insts_a, prop_normalize_a);
collect_insts (b, cell_b, flags, common_cell_indices_b, insts_b, prop_normalize_b);
collect_insts (a, cell_a, flags, common_cell_indices_a, insts_a, prop_normalize_a, no_duplicates);
collect_insts (b, cell_b, flags, common_cell_indices_b, insts_b, prop_normalize_b, no_duplicates);
std::vector <db::CellInstArrayWithProperties> anotb;
std::set_difference (insts_a.begin (), insts_a.end (), insts_b.begin (), insts_b.end (), std::back_inserter (anotb));
rewrite_instances_to (anotb, flags, common_cells_a, prop_remap_to_a);
collect_insts_of_unmapped_cells (a, cell_a, flags, common_cell_indices_a, anotb);
collect_insts_of_unmapped_cells (a, cell_a, flags, common_cell_indices_a, anotb, no_duplicates);
std::vector <db::CellInstArrayWithProperties> bnota;
std::set_difference (insts_b.begin (), insts_b.end (), insts_a.begin (), insts_a.end (), std::back_inserter (bnota));
rewrite_instances_to (bnota, flags, common_cells_b, prop_remap_to_b);
collect_insts_of_unmapped_cells (b, cell_b, flags, common_cell_indices_b, bnota);
collect_insts_of_unmapped_cells (b, cell_b, flags, common_cell_indices_b, bnota, no_duplicates);
if (! anotb.empty () || ! bnota.empty ()) {
@ -979,7 +1018,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
collect_polygons (b, cell_b, layer_b, flags, polygons_b, prop_normalize_b);
}
reduce (polygons_a, polygons_b, make_polygon_compare_func (tolerance), tolerance > 0);
reduce (polygons_a, polygons_b, make_polygon_compare_func (tolerance), tolerance > 0, no_duplicates);
if (!polygons_a.empty () || !polygons_b.empty ()) {
differs = true;
@ -1007,7 +1046,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
collect_paths (b, cell_b, layer_b, flags, paths_b, prop_normalize_b);
}
reduce (paths_a, paths_b, make_path_compare_func (tolerance), tolerance > 0);
reduce (paths_a, paths_b, make_path_compare_func (tolerance), tolerance > 0, no_duplicates);
if (!paths_a.empty () || !paths_b.empty ()) {
differs = true;
@ -1034,7 +1073,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
collect_texts (b, cell_b, layer_b, flags, texts_b, prop_normalize_b);
}
reduce (texts_a, texts_b, make_text_compare_func (tolerance), tolerance > 0);
reduce (texts_a, texts_b, make_text_compare_func (tolerance), tolerance > 0, no_duplicates);
if (!texts_a.empty () || !texts_b.empty ()) {
differs = true;
@ -1061,7 +1100,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
collect_boxes (b, cell_b, layer_b, flags, boxes_b, prop_normalize_b);
}
reduce (boxes_a, boxes_b, make_box_compare_func (tolerance), tolerance > 0);
reduce (boxes_a, boxes_b, make_box_compare_func (tolerance), tolerance > 0, no_duplicates);
if (!boxes_a.empty () || !boxes_b.empty ()) {
differs = true;
@ -1088,7 +1127,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
collect_edges (b, cell_b, layer_b, flags, edges_b, prop_normalize_b);
}
reduce (edges_a, edges_b, make_edge_compare_func (tolerance), tolerance > 0);
reduce (edges_a, edges_b, make_edge_compare_func (tolerance), tolerance > 0, no_duplicates);
if (!edges_a.empty () || !edges_b.empty ()) {
differs = true;
@ -1113,7 +1152,7 @@ do_compare_layouts (const db::Layout &a, const db::Cell *top_a, const db::Layout
collect_edge_pairs (b, cell_b, layer_b, flags, edge_pairs_b, prop_normalize_b);
}
reduce (edge_pairs_a, edge_pairs_b, make_edge_pair_compare_func (tolerance), tolerance > 0);
reduce (edge_pairs_a, edge_pairs_b, make_edge_pair_compare_func (tolerance), tolerance > 0, no_duplicates);
if (!edge_pairs_a.empty () || !edge_pairs_b.empty ()) {
differs = true;

View File

@ -74,12 +74,15 @@ const unsigned int f_paths_as_polygons = 0x100;
// Derive smart cell mapping instead of name mapping (available only if top cells are specified)
const unsigned int f_smart_cell_mapping = 0x200;
// Don't summarize missing layers
// Don't summarize missing layers - print them in detail
const unsigned int f_dont_summarize_missing_layers = 0x400;
// Ignore text details (font, size, presentation)
const unsigned int f_no_text_details = 0x800;
// Ignore duplicate instances or shapes
const unsigned int f_ignore_duplicates = 0x1000;
}
/**

View File

@ -81,6 +81,12 @@ inline bool needs_translate (object_tag<Sh> /*tag*/)
return tl::is_equal_type<typename shape_traits<Sh>::can_deref, tl::True> () || tl::is_equal_type<typename shape_traits<Sh>::is_array, tl::True> ();
}
inline bool type_mask_applies (const db::LayerBase *layer, unsigned int flags)
{
unsigned int tm = layer->type_mask ();
return (((flags & db::ShapeIterator::Properties) == 0 || (tm & db::ShapeIterator::Properties) != 0) && (flags & tm) != 0);
}
// ---------------------------------------------------------------------------------------
// layer_op implementation
@ -214,7 +220,13 @@ Shapes::insert (const Shapes &d)
}
void
Shapes::do_insert (const Shapes &d)
Shapes::insert (const Shapes &d, unsigned int flags)
{
do_insert (d, flags);
}
void
Shapes::do_insert (const Shapes &d, unsigned int flags)
{
// shortcut for "nothing to do"
if (d.empty ()) {
@ -228,10 +240,12 @@ Shapes::do_insert (const Shapes &d)
m_layers.reserve (d.m_layers.size ());
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
m_layers.push_back ((*l)->clone ());
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
manager ()->queue (this, new FullLayerOp (true, m_layers.back ()));
if (type_mask_applies (*l, flags)) {
m_layers.push_back ((*l)->clone ());
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
manager ()->queue (this, new FullLayerOp (true, m_layers.back ()));
}
}
}
@ -239,7 +253,9 @@ Shapes::do_insert (const Shapes &d)
} else {
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
(*l)->insert_into (this);
if (type_mask_applies (*l, flags)) {
(*l)->insert_into (this);
}
}
}
@ -247,14 +263,18 @@ Shapes::do_insert (const Shapes &d)
// the target is standalone - dereference
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
(*l)->deref_into (this);
if (type_mask_applies (*l, flags)) {
(*l)->deref_into (this);
}
}
} else {
// both shape containers are in separate spaces - translate
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
(*l)->translate_into (this, shape_repository (), array_repository ());
if (type_mask_applies (*l, flags)) {
(*l)->translate_into (this, shape_repository (), array_repository ());
}
}
}
@ -1046,6 +1066,41 @@ Shapes::clear ()
}
}
void
Shapes::clear (unsigned int flags)
{
if (!m_layers.empty ()) {
invalidate_state (); // HINT: must come before the change is done!
tl::vector<LayerBase *> new_layers;
for (tl::vector<LayerBase *>::const_iterator l = m_layers.end (); l != m_layers.begin (); ) {
// because the undo stack will do a push, we need to remove layers from the back (this is the last undo
// element to be executed)
--l;
if (type_mask_applies (*l, flags)) {
if (manager () && manager ()->transacting ()) {
check_is_editable_for_undo_redo ();
manager ()->queue (this, new FullLayerOp (false, (*l)));
} else {
delete *l;
}
} else {
new_layers.push_back (*l);
}
}
m_layers.swap (new_layers);
}
}
void Shapes::reset_bbox_dirty ()
{
set_dirty (false);

View File

@ -628,6 +628,18 @@ public:
*/
void insert (const Shapes &d);
/**
* @brief Insert all shapes from another container using the given shape types only
*
* This method insert all shapes from the given shape container.
*
* HINT: This method can duplicate shape containers from one layout to another.
* The current implementation does not translate property Id's.
* It is mainly intended for 1-to-1 copies of layouts where the whole
* property repository is copied.
*/
void insert (const Shapes &d, unsigned int types);
/**
* @brief Assignment operator with transformation
*
@ -1219,6 +1231,11 @@ public:
*/
void clear ();
/**
* @brief Clears the collection (given shape types only)
*/
void clear (unsigned int types);
/**
* @brief Report the type mask of the objects stored herein
*
@ -1515,7 +1532,7 @@ private:
db::Cell *mp_cell; // HINT: contains "dirty" in bit 0 and "editable" in bit 1
void invalidate_state ();
void do_insert (const Shapes &d);
void do_insert (const Shapes &d, unsigned int flags = db::ShapeIterator::All);
void check_is_editable_for_undo_redo () const;
// gets the layers array

View File

@ -1682,29 +1682,53 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"@param a The first of the layers to swap.\n"
"@param b The second of the layers to swap.\n"
) +
gsi::method ("move_layer", &db::Layout::move_layer, gsi::arg ("src"), gsi::arg ("dest"),
gsi::method ("move_layer", static_cast<void (db::Layout::*) (unsigned int, unsigned int)> (&db::Layout::move_layer), gsi::arg ("src"), gsi::arg ("dest"),
"@brief Moves a layer\n"
"\n"
"This method was introduced in version 0.19.\n"
"\n"
"Move a layer from the source to the target. The target is not cleared before, so that this method \n"
"merges shapes from the source with the target layer. The source layer is empty after that operation.\n"
"Moves a layer from the source to the destination layer. The target is not cleared before, so that this method \n"
"merges shapes from the source with the destination layer. The source layer is empty after that operation.\n"
"\n"
"@param src The layer index of the source layer.\n"
"@param dest The layer index of the destination layer.\n"
"\n"
"This method was introduced in version 0.19.\n"
) +
gsi::method ("copy_layer", &db::Layout::copy_layer, gsi::arg ("src"), gsi::arg ("dest"),
gsi::method ("move_layer", static_cast<void (db::Layout::*) (unsigned int, unsigned int, unsigned int)> (&db::Layout::move_layer), gsi::arg ("src"), gsi::arg ("dest"), gsi::arg ("flags"),
"@brief Moves a layer (selected shape types only)\n"
"\n"
"Moves a layer from the source to the destination layer. The target is not cleared before, so that this method \n"
"merges shapes from the source with the destination layer. The copied shapes are removed from the source layer.\n"
"\n"
"@param src The layer index of the source layer.\n"
"@param dest The layer index of the destination layer.\n"
"@param flags A combination of the shape type flags from \\Shapes, S... constants\n"
"\n"
"This method variant has been introduced in version 0.28.9.\n"
) +
gsi::method ("copy_layer", static_cast<void (db::Layout::*) (unsigned int, unsigned int)> (&db::Layout::copy_layer), gsi::arg ("src"), gsi::arg ("dest"),
"@brief Copies a layer\n"
"\n"
"This method was introduced in version 0.19.\n"
"\n"
"Copy a layer from the source to the target. The target is not cleared before, so that this method \n"
"merges shapes from the source with the target layer.\n"
"Copies a layer from the source to the destination layer. The destination layer is not cleared before, so that this method \n"
"merges shapes from the source with the destination layer.\n"
"\n"
"@param src The layer index of the source layer.\n"
"@param dest The layer index of the destination layer.\n"
"\n"
"This method was introduced in version 0.19.\n"
) +
gsi::method ("clear_layer", &db::Layout::clear_layer, gsi::arg ("layer_index"),
gsi::method ("copy_layer", static_cast<void (db::Layout::*) (unsigned int, unsigned int, unsigned int)> (&db::Layout::copy_layer), gsi::arg ("src"), gsi::arg ("dest"), gsi::arg ("flags"),
"@brief Copies a layer (selected shape types only)\n"
"\n"
"Copies a layer from the source to the destination layer. The destination layer is not cleared before, so that this method \n"
"merges shapes from the source with the destination layer.\n"
"\n"
"@param src The layer index of the source layer.\n"
"@param dest The layer index of the destination layer.\n"
"@param flags A combination of the shape type flags from \\Shapes, S... constants\n"
"\n"
"This method variant has been introduced in version 0.28.9.\n"
) +
gsi::method ("clear_layer", static_cast<void (db::Layout::*) (unsigned int)> (&db::Layout::clear_layer), gsi::arg ("layer_index"),
"@brief Clears a layer\n"
"\n"
"Clears the layer: removes all shapes.\n"
@ -1712,6 +1736,16 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"This method was introduced in version 0.19.\n"
"\n"
"@param layer_index The index of the layer to delete.\n"
) +
gsi::method ("clear_layer", static_cast<void (db::Layout::*) (unsigned int, unsigned int)> (&db::Layout::clear_layer), gsi::arg ("layer_index"), gsi::arg ("flags"),
"@brief Clears a layer (given shape types only)\n"
"\n"
"Clears the layer: removes all shapes for the given shape types.\n"
"\n"
"This method was introduced in version 0.28.9.\n"
"\n"
"@param layer_index The index of the layer to delete.\n"
"@param flags The type selector for the shapes to delete (see \\Shapes class, S... constants).\n"
) +
gsi::method ("delete_layer", &db::Layout::delete_layer, gsi::arg ("layer_index"),
"@brief Deletes a layer\n"

View File

@ -397,6 +397,10 @@ static unsigned int f_silent () {
return db::layout_diff::f_silent;
}
static unsigned int f_ignore_duplicates () {
return db::layout_diff::f_ignore_duplicates;
}
static unsigned int f_no_text_orientation () {
return db::layout_diff::f_no_text_orientation;
}
@ -448,6 +452,13 @@ gsi::Class<LayoutDiff> decl_LayoutDiff ("db", "LayoutDiff",
"This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be "
"compared with other constants to form a flag set."
) +
gsi::constant ("IgnoreDuplicates", &f_ignore_duplicates,
"@brief Ignore duplicate instances or shapes\n"
"With this option present, duplicate instances or shapes are ignored and "
"duplication does not count as a difference.\n"
"\n"
"This option has been introduced in version 0.28.9."
) +
gsi::constant ("NoTextOrientation", &f_no_text_orientation,
"@brief Ignore text orientation\n"
"This constant can be used for the flags parameter of \\compare_layouts and \\compare_cells. It can be "

View File

@ -108,47 +108,47 @@ static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin (const db
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_all (const db::Shapes *s)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_all (const db::Shapes *s)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin (db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_overlapping_all (const db::Shapes *s, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_overlapping_all (const db::Shapes *s, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (region, db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_doverlapping_all (const db::Shapes *s, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_doverlapping_all (const db::Shapes *s, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_touching (const db::Shapes *s, unsigned int flags, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching (const db::Shapes *s, unsigned int flags, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_touching_all (const db::Shapes *s, const db::Box &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_touching_all (const db::Shapes *s, const db::Box &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (region, db::ShapeIterator::All));
}
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator>begin_dtouching_all (const db::Shapes *s, const db::DBox &region)
static gsi::layout_locking_iterator1<db::Shapes::shape_iterator> begin_dtouching_all (const db::Shapes *s, const db::DBox &region)
{
return gsi::layout_locking_iterator1<db::Shapes::shape_iterator> (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All));
}
@ -251,12 +251,7 @@ static void insert_shapes (db::Shapes *sh, const db::Shapes &s)
static void insert_shapes_with_flags (db::Shapes *sh, const db::Shapes &s, unsigned int flags)
{
// 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 (sh->layout ());
for (db::Shapes::shape_iterator i = s.begin (flags); !i.at_end(); ++i) {
sh->insert (*i);
}
sh->insert (s, flags);
}
static void insert_shapes_with_trans (db::Shapes *sh, const db::Shapes &s, const db::ICplxTrans &trans)
@ -1264,9 +1259,15 @@ Class<db::Shapes> decl_Shapes ("db", "Shapes",
"@brief Returns a value indicating whether the shapes container is empty\n"
"This method has been introduced in version 0.20.\n"
) +
gsi::method ("clear", &db::Shapes::clear,
gsi::method ("clear", static_cast<void (db::Shapes::*) ()> (&db::Shapes::clear),
"@brief Clears the shape container\n"
"This method has been introduced in version 0.16. It can only be used in editable mode."
"This method has been introduced in version 0.16."
) +
gsi::method ("clear", static_cast<void (db::Shapes::*) (unsigned int)> (&db::Shapes::clear), gsi::arg ("flags"),
"@brief Clears certain shape types from the shape container\n"
"Only shapes matching the shape types from 'flags' are removed. 'flags' is a combination of the S... constants.\n"
"\n"
"This method has been introduced in version 0.28.9."
) +
gsi::method_ext ("size", &shapes_size,
"@brief Gets the number of shapes in this container\n"
@ -1296,10 +1297,19 @@ Class<db::Shapes> decl_Shapes ("db", "Shapes",
"returned for future references."
) +
gsi::method ("SAll|#s_all", &s_all,
"@brief Indicates that all shapes shall be retrieved"
"@brief Indicates that all shapes shall be retrieved\n"
"You can use this constant to construct 'except' classes - e.g. "
"to specify 'all shape types except boxes' use\n"
"\n"
"@code SAll - SBoxes @/code\n"
) +
gsi::method ("SAllWithProperties|#s_all_with_properties", &s_all_with_properties,
"@brief Indicates that all shapes with properties shall be retrieved"
"@brief Indicates that all shapes with properties shall be retrieved\n"
"Using this selector means to retrieve only shapes with properties."
"You can use this constant to construct 'except' classes - e.g. "
"to specify 'all shape types with properties except boxes' use\n"
"\n"
"@code SAllWithProperties - SBoxes @/code\n"
) +
gsi::method ("SPolygons|#s_polygons", &s_polygons,
"@brief Indicates that polygons shall be retrieved"
@ -1333,7 +1343,10 @@ Class<db::Shapes> decl_Shapes ("db", "Shapes",
"@brief Indicates that user objects shall be retrieved"
) +
gsi::method ("SProperties|#s_properties", &s_properties,
"@brief Indicates that only shapes with properties shall be retrieved"
"@brief Indicates that only shapes with properties shall be retrieved\n"
"You can or-combine this flag with the plain shape types to select a "
"certain shape type, but only those shapes with properties. For example to "
"select boxes with properties, use 'SProperties | SBoxes'."
) +
gsi::method_ext ("dump_mem_statistics", &dump_mem_statistics, gsi::arg<bool> ("detailed", false),
"@hide"

View File

@ -938,6 +938,7 @@ TEST(two_1)
db::box_convert<db::Box> bc1;
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
bs.set_scanner_threshold1 (0);
bs.process (tr, 1, bc1, bc2);
EXPECT_EQ (tr.str, "[i](2-12)(2-14)(4-12)(4-14)(2-15)(4-15)(5-12)(5-14)(5-15)(2-13)(4-13)(3-12)(3-14)(3-13)(3-15)(5-13)(0-10)<2><5><4><3><12><15><14><13>(0-11)(1-10)(1-11)<0><1><10><11>[f]");
}
@ -974,6 +975,7 @@ TEST(two_1a)
db::box_convert<db::Box> bc1;
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
bs.set_scanner_threshold1 (0);
bs.process (tr, 1, bc1, bc2);
EXPECT_EQ (tr.str, "[i](2-11)(2-12)(1-11)(1-12)<1><2><11><12>(0-10)<0><10>[f]");
}
@ -1010,6 +1012,7 @@ TEST(two_1b)
db::box_convert<db::Box> bc1;
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
bs.set_scanner_threshold1 (0);
EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true);
EXPECT_EQ (tr.str, "[i](1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>[f]");
@ -1046,14 +1049,15 @@ TEST(two_1c)
db::box_convert<db::Box> bc1;
db::box_convert<db::SimplePolygon> bc2;
bs.set_scanner_threshold (0);
bs.set_scanner_threshold1 (0);
EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true);
EXPECT_EQ (tr.str, "[i]<0><10>(1-12)(2-12)(1-11)(2-11)<1><2><12><11>[f]");
}
void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true)
void run_test2_two (tl::TestBase *_this, size_t n1, size_t n2, double ff, db::Coord spread, bool touch = true, bool no_shortcut = true)
{
std::vector<db::Box> bb;
for (size_t i = 0; i < n; ++i) {
for (size_t i = 0; i < n1; ++i) {
db::Coord x = rand () % spread;
db::Coord y = rand () % spread;
bb.push_back (db::Box (x, y, x + 100, y + 100));
@ -1061,7 +1065,7 @@ void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread,
}
std::vector<db::SimplePolygon> bb2;
for (size_t i = 0; i < n; ++i) {
for (size_t i = 0; i < n2; ++i) {
db::Coord x = rand () % spread;
db::Coord y = rand () % spread;
bb2.push_back (db::SimplePolygon (db::Box (x, y, x + 100, y + 100)));
@ -1082,7 +1086,10 @@ void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread,
db::box_convert<db::SimplePolygon> bc2;
{
tl::SelfTimer timer ("box-scanner");
bs.set_scanner_threshold (0);
if (no_shortcut) {
bs.set_scanner_threshold (0);
bs.set_scanner_threshold1 (0);
}
bs.process (tr, touch ? 1 : 0, bc1, bc2);
}
@ -1118,45 +1125,60 @@ void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread,
TEST(two_2a)
{
run_test2_two(_this, 10, 0.0, 1000);
run_test2_two(_this, 10, 10, 0.0, 1000);
run_test2_two(_this, 10, 10, 0.0, 1000, true, false /*sub-threshold*/);
}
TEST(two_2b)
{
run_test2_two(_this, 10, 0.0, 100);
run_test2_two(_this, 10, 10, 0.0, 100);
run_test2_two(_this, 10, 10, 0.0, 100, true, false /*sub-threshold*/);
}
TEST(two_2c)
{
run_test2_two(_this, 10, 0.0, 10);
run_test2_two(_this, 10, 10, 0.0, 10);
run_test2_two(_this, 10, 10, 0.0, 10, true, false /*sub-threshold*/);
}
TEST(two_2d)
{
run_test2_two(_this, 1000, 0.0, 1000);
run_test2_two(_this, 1000, 1000, 0.0, 1000);
}
TEST(two_2e)
{
run_test2_two(_this, 1000, 2, 1000);
run_test2_two(_this, 1000, 1000, 2, 1000);
}
TEST(two_2f)
{
run_test2_two(_this, 1000, 2, 1000, false);
run_test2_two(_this, 1000, 1000, 2, 1000, false);
}
TEST(two_2g)
{
run_test2_two(_this, 1000, 2, 500);
run_test2_two(_this, 1000, 1000, 2, 500);
}
TEST(two_2h)
{
run_test2_two(_this, 1000, 2, 100);
run_test2_two(_this, 1000, 1000, 2, 100);
}
TEST(two_2i)
{
run_test2_two(_this, 10000, 2, 10000);
run_test2_two(_this, 10000, 1000, 2, 10000);
}
TEST(two_2j)
{
run_test2_two(_this, 3, 1000, 0.0, 1000);
run_test2_two(_this, 3, 1000, 0.0, 1000, true, false /*sub-threshold*/);
}
TEST(two_2k)
{
run_test2_two(_this, 1000, 3, 0.0, 1000);
run_test2_two(_this, 1000, 3, 0.0, 1000, true, false /*sub-threshold*/);
}

View File

@ -1278,3 +1278,9 @@ TEST(FlatOperation)
run_test_bool22_flat (_this, "hlp17_flat.oas", TMAndNot, 100, 101);
}
TEST(Arrays)
{
// Large arrays, NOT
run_test_bool2 (_this, "hlp18.oas", TMNot, 100);
}

View File

@ -105,6 +105,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties &
}
if (ci.properties_id () != 0) {
m_os << " [" << ci.properties_id () << "]" << std::endl;
} else {
m_os << "" << std::endl;
}
}
@ -489,7 +491,9 @@ TEST(1)
" c4 m45 *1 -10,20\n"
" c4 m45 *1 -10,20\n"
"Not in b but in a:\n"
" c5x r0 *1 10,-20 c5x m45 *1 -10,20Not in a but in b:\n"
" c5x r0 *1 10,-20\n"
" c5x m45 *1 -10,20\n"
"Not in a but in b:\n"
);
g = h;
@ -951,7 +955,7 @@ TEST(3)
c2h.shapes (0).insert (db::Polygon (db::Box (1, 2, 1003, 1006)));
r.clear ();
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r);
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r);
EXPECT_EQ (eq, false);
EXPECT_EQ (r.text (),
@ -965,7 +969,7 @@ TEST(3)
);
r.clear ();
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r);
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 1, r);
EXPECT_EQ (eq, false);
EXPECT_EQ (r.text (),
@ -1499,4 +1503,205 @@ TEST(7)
EXPECT_EQ (r.text (), "");
}
TEST(8)
{
db::Layout g;
g.insert_layer (0);
g.set_properties (0, db::LayerProperties (17, 0));
g.insert_layer (1);
g.set_properties (1, db::LayerProperties (42, 1));
db::cell_index_type c1i = g.add_cell ("c1");
db::cell_index_type c2i = g.add_cell ("c2x");
db::cell_index_type c3i = g.add_cell ("c3");
db::cell_index_type c4i = g.add_cell ("c4");
db::cell_index_type c5i = g.add_cell ("c5x");
{
db::Cell &c1 (g.cell (c1i));
db::Cell &c2 (g.cell (c2i));
db::Cell &c3 (g.cell (c3i));
db::Cell &c4 (g.cell (c4i));
db::Cell &c5 (g.cell (c5i));
c2.shapes (0).insert (db::Box (0, 1, 2, 3));
db::FTrans f (1, true);
db::Vector p (-10, 20);
db::Trans t (f.rot (), p);
db::Vector pp (10, -20);
db::Trans tt (0, pp);
// c4->c1 (aref)
c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
// c5->c1
c5.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
// c3->c5 (3x)
c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), tt));
c3.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c5.cell_index ()), t));
// c4->c3
c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c3.cell_index ()), t));
// c4->c1
c4.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
// c2->c1 (2x)
c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), t));
c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1.cell_index ()), tt));
// c2->c4 (2x)
c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
c2.insert (db::array <db::CellInst, db::Trans> (db::CellInst (c4.cell_index ()), t));
}
db::Layout h = g;
TestDifferenceReceiver r;
bool eq;
g.cell (c2i).shapes (0).insert (db::Box (1, 2, 1001, 1002));
g.cell (c2i).shapes (0).insert (db::Box (2, 3, 1002, 1003));
g.cell (c2i).shapes (0).insert (db::Box (2, 3, 1002, 1003));
g.cell (c2i).shapes (0).insert (db::Box (3, 4, 1003, 1004));
g.cell (c2i).shapes (0).insert (db::Box (3, 4, 1003, 1004));
h.cell (c2i).shapes (0).insert (db::Box (1, 2, 1001, 1002));
h.cell (c2i).shapes (0).insert (db::Box (1, 2, 1001, 1002));
h.cell (c2i).shapes (0).insert (db::Box (2, 3, 1002, 1003));
h.cell (c2i).shapes (0).insert (db::Box (4, 5, 1004, 1005));
h.cell (c2i).shapes (0).insert (db::Box (4, 5, 1004, 1005));
r.clear ();
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r);
EXPECT_EQ (eq, false);
EXPECT_EQ (r.text (),
"layout_diff: boxes differ for layer 17/0 in cell c2x\n"
"Not in b but in a:\n"
" (2,3;1002,1003)\n"
" (3,4;1003,1004)\n"
" (3,4;1003,1004)\n"
"Not in a but in b:\n"
" (1,2;1001,1002)\n"
" (4,5;1004,1005)\n"
" (4,5;1004,1005)\n"
);
r.clear ();
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose + db::layout_diff::f_ignore_duplicates, 0, r);
EXPECT_EQ (eq, false);
EXPECT_EQ (r.text (),
"layout_diff: boxes differ for layer 17/0 in cell c2x\n"
"Not in b but in a:\n"
" (3,4;1003,1004)\n"
"Not in a but in b:\n"
" (4,5;1004,1005)\n"
);
// duplicate instances
{
db::FTrans f (1, true);
db::Vector p (-10, 20);
db::Trans t (f.rot (), p);
h.cell(c4i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
h.cell(c4i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t));
h.cell(c4i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c1i), t, db::Vector(1, 1), db::Vector (0, 2), 2, 3));
db::cell_index_type c6i = g.add_cell ("c6");
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c6i), t));
g.cell(c5i).insert (db::array <db::CellInst, db::Trans> (db::CellInst (c6i), t));
}
r.clear ();
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose, 0, r);
EXPECT_EQ (eq, false);
EXPECT_EQ (r.text (),
"layout_diff: cell c6 is not present in layout b, but in a\n"
"layout_diff: boxes differ for layer 17/0 in cell c2x\n"
"Not in b but in a:\n"
" (2,3;1002,1003)\n"
" (3,4;1003,1004)\n"
" (3,4;1003,1004)\n"
"Not in a but in b:\n"
" (1,2;1001,1002)\n"
" (4,5;1004,1005)\n"
" (4,5;1004,1005)\n"
"layout_diff: instances differ in cell c4\n"
"list for a:\n"
" c1 r0 *1 10,-20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c3 m45 *1 -10,20\n"
"list for b:\n"
" c1 r0 *1 10,-20\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c3 m45 *1 -10,20\n"
"Not in b but in a:\n"
"Not in a but in b:\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
"layout_diff: instances differ in cell c5x\n"
"list for a:\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
"list for b:\n"
" c1 m45 *1 -10,20\n"
"Not in b but in a:\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c6 m45 *1 -10,20\n"
" c6 m45 *1 -10,20\n"
"Not in a but in b:\n"
);
r.clear ();
eq = db::compare_layouts (g, h, db::layout_diff::f_verbose + db::layout_diff::f_ignore_duplicates, 0, r);
EXPECT_EQ (eq, false);
EXPECT_EQ (r.text (),
"layout_diff: cell c6 is not present in layout b, but in a\n"
"layout_diff: boxes differ for layer 17/0 in cell c2x\n"
"Not in b but in a:\n"
" (3,4;1003,1004)\n"
"Not in a but in b:\n"
" (4,5;1004,1005)\n"
"layout_diff: instances differ in cell c4\n"
"list for a:\n"
" c1 r0 *1 10,-20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c3 m45 *1 -10,20\n"
"list for b:\n"
" c1 r0 *1 10,-20\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c3 m45 *1 -10,20\n"
"Not in b but in a:\n"
"Not in a but in b:\n"
" c1 m45 *1 -10,20\n"
"layout_diff: instances differ in cell c5x\n"
"list for a:\n"
" c1 m45 *1 -10,20\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
"list for b:\n"
" c1 m45 *1 -10,20\n"
"Not in b but in a:\n"
" c1 m45 *1 -10,20[a=1,1, b=0,2, na=2, nb=3]\n"
" c6 m45 *1 -10,20\n"
"Not in a but in b:\n"
);
}

View File

@ -1925,6 +1925,51 @@ TEST(7)
}
}
// copy, move, clear with shape types
TEST(8)
{
db::Manager m (true);
db::Layout layout (true, &m);
unsigned int lindex1 = layout.insert_layer ();
unsigned int lindex2 = layout.insert_layer ();
db::Cell &topcell = layout.cell (layout.add_cell ("TOP"));
topcell.shapes (lindex1).insert (db::Box (1, 2, 3, 4));
topcell.shapes (lindex1).insert (db::Polygon (db::Box (1, 2, 3, 4)));
{
db::Transaction trans (&m, "T1");
topcell.shapes (lindex2).insert (topcell.shapes (lindex1));
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "polygon (1,2;1,4;3,4;3,2) #0\nbox (1,2;3,4) #0\n");
}
m.undo ();
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "");
{
db::Transaction trans (&m, "T1");
topcell.shapes (lindex2).insert (topcell.shapes (lindex1), db::ShapeIterator::Boxes);
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "box (1,2;3,4) #0\n");
}
m.undo ();
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "");
topcell.shapes (lindex2).insert (topcell.shapes (lindex1));
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "polygon (1,2;1,4;3,4;3,2) #0\nbox (1,2;3,4) #0\n");
{
db::Transaction trans (&m, "T1");
topcell.shapes (lindex2).clear (db::ShapeIterator::Polygons);
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), "box (1,2;3,4) #0\n");
}
m.undo ();
EXPECT_EQ (shapes_to_string (_this, topcell.shapes (lindex2)), shapes_to_string (_this, topcell.shapes (lindex1)));
}
TEST(10A)
{
if (db::default_editable_mode ()) {

View File

@ -1182,6 +1182,36 @@ See <a href="/about/drc_ref_netter.xml#netlist">Netter#netlist</a> for a descrip
<p>
See <a href="/about/drc_ref_netter.xml">Netter</a> for more details
</p>
<a name="new_report"/><h2>"new_report" - Creates a new report database object for use in "output"</h2>
<keyword name="new_report"/>
<p>Usage:</p>
<ul>
<li><tt>new_report(description [, filename [, cellname ] ])</tt></li>
</ul>
<p>
This method creates an independent report object. This object
can be used in "output" to send a layer to a different report than
the default report or target.
</p><p>
Arguments are the same than for <a href="/about/drc_ref_global.xml#report">report</a>.
</p><p>
See <a href="/about/drc_ref_layer.xml#output">Layer#output</a> for details about this feature.
</p>
<a name="new_target"/><h2>"new_target" - Creates a new layout target object for use in "output"</h2>
<keyword name="new_target"/>
<p>Usage:</p>
<ul>
<li><tt>new_target(what [, cellname])</tt></li>
</ul>
<p>
This method creates an independent target object. This object
can be used in "output" to send a layer to a different layout file than
the default report or target.
</p><p>
Arguments are the same than for <a href="/about/drc_ref_global.xml#target">target</a>.
</p><p>
See <a href="/about/drc_ref_layer.xml#output">Layer#output</a> for details about this feature.
</p>
<a name="no_borders"/><h2>"no_borders" - Reset the tile borders</h2>
<keyword name="no_borders"/>
<p>Usage:</p>
@ -1413,6 +1443,21 @@ See <a href="/about/drc_ref_source.xml#polygons">Source#polygons</a> for a descr
The primary input of the universal DRC function is the layer the <a href="/about/drc_ref_layer.xml#drc">Layer#drc</a> function
is called on.
</p>
<a name="profile"/><h2>"profile" - Profiles the script and provides a runtime + memory statistics</h2>
<keyword name="profile"/>
<p>Usage:</p>
<ul>
<li><tt>profile</tt></li>
<li><tt>profile(n)</tt></li>
</ul>
<p>
Turns profiling on or off (default). In profiling mode, the
system will collect statistics about rules executed, their execution time
and memory information. The argument specifies how many operations to
print at the end of the run. Without an argument, all operations are
printed. Passing "false" for the argument will disable profiling. This is the
default.
</p>
<a name="props_copy"/><h2>"props_copy" - Specifies "copy properties" on operations supporting user properties constraints</h2>
<keyword name="props_copy"/>
<p>

View File

@ -2217,6 +2217,26 @@ a single <class_doc href="LayerInfo">LayerInfo</class_doc> object.
</p><p>
See <a href="/about/drc_ref_global.xml#report">report</a> and <a href="/about/drc_ref_global.xml#target">target</a> on how to configure output to a target layout
or report database.
</p><p>
See also <a href="/about/drc_ref_global.xml#new_target">new_target</a> and <a href="/about/drc_ref_global.xml#new_report">new_report</a> on how to create additional
targets for output. This allows saving certain layers to different files than
the standard target or report. To do so, create a new target or report using one
of these functions and pass that object to the corresponding "output" call as
an additional argument.
</p><p>
Example:
</p><p>
<pre>
check1 = ...
check2 = ...
check3 = ...
second_report = new_report("Only for check2", "check2.lyrdb")
check1.output("Check 1")
check2.output("Check 2", second_report)
check3.output("Check 3")
</pre>
</p>
<a name="outside"/><h2>"outside" - Selects edges or polygons of self which are outside edges or polygons from the other layer</h2>
<keyword name="outside"/>

View File

@ -185,7 +185,7 @@ l.output("OUT")
l.output(17, 0, "OUT")</pre>
<p>
Output can be sent to other layouts using the "target" function:
Output can be sent to other layouts using the <a href="/about/drc_ref_global.xml#target">target</a> function:
</p>
<pre>
@ -196,7 +196,7 @@ target("@2")
target("out.gds", "OUT_TOP")</pre>
<p>
Output can also be sent to a report database:
Output can also be sent to a report database using the <a href="/about/drc_ref_global.xml#report">report</a> function:
</p>
<pre>
@ -234,6 +234,38 @@ l.output("check1", "The first check")</pre>
unpredictable.
</p>
<p>
It is possible to open "side" reports and targets and send layers to these
outputs without closing the default output.
</p>
<p>
To open a "side report", use <a href="/about/drc_ref_global.xml#new_report">new_report</a>
in the same way you use "report". Instead of switching the output, this function will return a
new report object that can be included in the argument list of "output"
for the layer that is to be sent to that side report:
</p>
<pre>
# opens a new side report
side_report = new_report("Another report")
...
# Send data from layer l to new category "check1" to the side report
l.output(side_report, "check1", "The first check")</pre>
<p>
In the same way, "side targets" can be opened using <a href="/about/drc_ref_global.xml#new_target">new_target</a>.
Such side targets open a way to write certain layers to other layout files.
This is very handy for debugging:
</p>
<pre>
# opens a new side target for debugging
debug_out = new_target("debug.gds")
...
# Send data from layer l to the debug output, layer 100/0
l.output(debug_out, 100, 0)</pre>
<h2>Dimension specifications</h2>
@ -710,12 +742,12 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
layer = input(1, 0)
layer.raw.sized(0.1).output(100, 0)
layer = input(1, 0)
layer.raw.sized(0.1).output(100, 0)
# this check will now be done on a raw layer, since the
# previous raw call was putting the layer into raw mode
layer.width(0.2).ouput(101, 0)</pre>
# this check will now be done on a raw layer, since the
# previous raw call was putting the layer into raw mode
layer.width(0.2).ouput(101, 0)</pre>
<p>
The following two images show the effect of raw and clean mode:
@ -792,20 +824,18 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
drc_w = input(1, 0).width(0.2)
...
</pre>
...
drc_w = input(1, 0).width(0.2)
...</pre>
<p>
can be written as:
</p>
<pre>
...
drc_w = input(1, 0).drc(width &lt; 0.2)
...
</pre>
...
drc_w = input(1, 0).drc(width &lt; 0.2)
...</pre>
<p>
The <a href="/about/drc_ref_layer.xml#drc">drc</a> method is the "universal DRC" method.
@ -845,12 +875,11 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
l1 = input(1, 0)
l2 = input(2, 0)
drc_sep = l1.drc(separation(l2) &lt;= 0.5)
...
</pre>
...
l1 = input(1, 0)
l2 = input(2, 0)
drc_sep = l1.drc(separation(l2) &lt;= 0.5)
...</pre>
<p>
Options are also specified together with the measurement and follow the same notation
@ -858,10 +887,9 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
drc_w = input(1, 0).drc(width(projection) &lt; 0.2)
...
</pre>
...
drc_w = input(1, 0).drc(width(projection) &lt; 0.2)
...</pre>
<p>
However, the universal DRC is much more than a convenient way to write checks:
@ -879,10 +907,9 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
drc_ws = input(1, 0).drc((width &lt; 0.2) &amp; (space &lt; 0.3))
...
</pre>
...
drc_ws = input(1, 0).drc((width &lt; 0.2) &amp; (space &lt; 0.3))
...</pre>
<p>
The boolean AND is computed between the edges on the primary shape and returns the
@ -891,12 +918,11 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
drc_ws1 = input(1, 0).width(0.2).edges
drc_ws2 = input(1, 0).space(0.3).edges
drc_ws = drc_ws1 &amp; drc_ws2
...
</pre>
...
drc_ws1 = input(1, 0).width(0.2).edges
drc_ws2 = input(1, 0).space(0.3).edges
drc_ws = drc_ws1 &amp; drc_ws2
...</pre>
<p>
The reason is that performing the boolean computation in the local loop can be
@ -933,11 +959,10 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
...
drc_w = input(1, 0).width(0.2)
log("Number of width violations: #{drc_w.data.size}")
...
</pre>
...
drc_w = input(1, 0).width(0.2)
log("Number of width violations: #{drc_w.data.size}")
...</pre>
<p>
The <a href="/about/drc_ref_global.xml#error">error</a> function can be used to output error messages
@ -946,11 +971,23 @@ overlaps = layer.size(0.2).raw.merged(2)</pre>
</p>
<pre>
log_file("drc_log.txt")
verbose(true)
info("This message will be sent to the log file")
...
</pre>
log_file("drc_log.txt")
verbose(true)
info("This message will be sent to the log file")
...</pre>
<p>
The <a href="/about/drc_ref_global.xml#profile">profile</a> function will collect profiling
information during the DRC run. At the end of the script, the operations are printed to the log
output, sorted by their CPU time and approximate memory footprint. "profile" can be given a
numerical argument indicating the number of operations to print. Lower-ranking operations are
skipped in that case. By default, all operations are printed.
</p>
<pre>
# enables profiling
profile
...</pre>
<h2>The tiling option</h2>
@ -1003,8 +1040,7 @@ threads(4)
# Disable tiling
flat
... non-tiled operations ...
</pre>
... non-tiled operations ...</pre>
<p>
Some operations implicitly specify a tile border. If the tile border is known (see length example above), explicit borders

View File

@ -13,6 +13,125 @@ module DRC
end
end
class OutputChannel
def initialize(engine)
@engine = engine
end
def is_rdb?
false
end
def cellname=(cellname)
# reimplement
end
def cell
# reimplement
end
def write
# reimplement
end
def finish(final)
# reimplement
end
def write
# reimplement
end
def layout
nil
end
def rdb
nil
end
end
class LayoutOutputChannel < OutputChannel
def initialize(engine, layout, cell, file_name)
super(engine)
@layout = layout
@cell = cell
@file_name = file_name
end
def cellname=(cellname)
@cell = @layout.cell(cellname) || @layout.create_cell(cellname)
end
def is_rdb?
false
end
def finish(final, view)
write
end
def write
if @file_name
opt = RBA::SaveLayoutOptions::new
gzip = opt.set_format_from_filename(@file_name)
@engine.info("Writing layout file: #{@file_name} ..")
@layout.write(@file_name, gzip, opt)
end
end
def layout
@layout
end
def cell
@cell
end
end
class RDBOutputChannel < OutputChannel
def initialize(engine, rdb, rdb_index, cellname, file_name)
super(engine)
@rdb = rdb
@rdb_index = rdb_index
@cell = cellname && rdb.create_cell(cellname)
@file_name = file_name
end
def cellname=(cellname)
@cell = nil
@rdb.each_cell do |c|
if c.name == cellname
@cell = c
end
end
@cell ||= @rdb.create_cell(cellname)
end
def is_rdb?
true
end
def finish(final, view)
write
if final && view
view.show_rdb(@rdb_index, view.active_cellview_index)
end
end
def write
if @file_name
rdb_file = @engine._make_path(@file_name)
@engine.info("Writing report database: #{rdb_file} ..")
@rdb.save(rdb_file)
end
end
def rdb
@rdb
end
def cell
@cell
end
end
# The DRC engine
# %DRC%
@ -40,11 +159,9 @@ module DRC
@def_source = nil
@dbu_read = false
use_dbu(@def_layout && @def_layout.dbu)
@output_layout = nil
@output_rdb = nil
@output_rdb_file = nil
@output_rdb_cell = nil
@show_l2ndb = nil
@def_output = nil
@other_outputs = []
@output_l2ndb_file = nil
@target_netlist_file = nil
@target_netlist_format = nil
@ -70,6 +187,9 @@ module DRC
dss._destroy
@verbose = false
@profile = false
@profile_n = 0
@profile_info = {}
@in_context = nil
@ -711,6 +831,26 @@ module DRC
end
end
# %DRC%
# @name profile
# @brief Profiles the script and provides a runtime + memory statistics
# @synopsis profile
# @synopsis profile(n)
# Turns profiling on or off (default). In profiling mode, the
# system will collect statistics about rules executed, their execution time
# and memory information. The argument specifies how many operations to
# print at the end of the run. Without an argument, all operations are
# printed. Passing "false" for the argument will disable profiling. This is the
# default.
def profile(n = 0)
if !n.is_a?(1.class) && n != nil && n != false
raise("Argument to 'profile' must be either an integer number or nil")
end
@profile = !!n
@profile_n = [n || 0, 0].max
end
# %DRC%
# @name verbose?
# @brief Returns true, if verbose mode is enabled
@ -1340,46 +1480,42 @@ module DRC
self._context("report") do
@output_rdb_file = filename
# finish what we got so far
_finish(false)
name = filename && File::basename(filename)
name ||= "DRC"
@output_rdb_index = nil
view = RBA::LayoutView::current
if view
if self._rdb_index
@output_rdb = RBA::ReportDatabase::new("") # reuse existing name
@output_rdb_index = view.replace_rdb(self._rdb_index, @output_rdb)
else
@output_rdb = RBA::ReportDatabase::new(name)
@output_rdb_index = view.add_rdb(@output_rdb)
end
else
@output_rdb = RBA::ReportDatabase::new(name)
end
@output_layout = nil
@output_cell = nil
@output_layout_file = nil
cn = nil
cn ||= @def_cell && @def_cell.name
cn ||= source && source.cell_name
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")
@output_rdb_cell = @output_rdb.create_cell(cn)
@output_rdb.generator = self._generator
@output_rdb.top_cell_name = cn
@output_rdb.description = description
@def_output = nil
@def_output = _make_report(description, filename, cellname)
end
end
# %DRC%
# @name new_report
# @brief Creates a new report database object for use in "output"
# @synopsis new_report(description [, filename [, cellname ] ])
#
# This method creates an independent report object. This object
# can be used in "output" to send a layer to a different report than
# the default report or target.
#
# Arguments are the same than for \global#report.
#
# See \Layer#output for details about this feature.
def new_report(description, filename = nil, cellname = nil)
output = nil
self._context("new_report") do
output = _make_report(description, filename, cellname)
@other_outputs << output
end
output
end
# %DRC%
# @name report_netlist
# @brief Specifies an extracted netlist report for output
@ -1456,25 +1592,13 @@ module DRC
# finish what we got so far
_flush
if @output_rdb
cell = nil
@output_rdb.each_cell do |c|
if c.name == cellname
cell = c
end
if ! @def_output
if @def_layout
# establish a new default output from the default layout on this occasion
@def_output = LayoutOutputChannel::new(self, @def_layout, cellname.to_s, nil)
end
cell ||= @output_rdb.create_cell(cellname)
@output_rdb_cell = cell
else
@output_layout ||= @def_layout
if @output_layout
@output_cell = @output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)
end
@def_output.cellname = cellname.to_s
end
end
@ -1512,51 +1636,40 @@ module DRC
# finish what we got so far
_finish(false)
if arg.is_a?(String)
if arg =~ /^@(\d+|\+)/
view = RBA::LayoutView::current
view || raise("No view open")
if $1 == "+"
n = view.create_layout(true)
cellname ||= (@def_cell ? @def_cell.name : "TOP")
else
n = $1.to_i - 1
end
(n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}")
cv = view.cellview(n)
cv.is_valid? || raise("Invalid layout @#{n + 1}")
@output_layout = cv.layout
@output_cell = cellname ? (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) : cv.cell
cv.cell = @output_cell
@output_layout_file = nil
else
@output_layout = RBA::Layout::new
@output_cell = cellname && @output_layout.create_cell(cellname.to_s)
@output_layout_file = arg
end
elsif arg.is_a?(RBA::Layout)
@output_layout = arg
@output_cell = cellname && (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s))
@output_layout_file = nil
elsif arg.is_a?(RBA::Cell)
@output_layout = arg.layout
@output_cell = arg
@output_layout_file = nil
else
raise("Invalid argument '" + arg.inspect + "'")
end
@def_output = nil
@def_output = _make_target(arg, cellname)
end
end
# %DRC%
# @name new_target
# @brief Creates a new layout target object for use in "output"
# @synopsis new_target(what [, cellname])
#
# This method creates an independent target object. This object
# can be used in "output" to send a layer to a different layout file than
# the default report or target.
#
# Arguments are the same than for \global#target.
#
# See \Layer#output for details about this feature.
def new_target(arg, cellname = nil)
output = nil
self._context("new_target") do
output = _make_target(arg, cellname)
@other_outputs << output
end
output
end
# %DRC%
# @name box
# @brief Creates a box object
@ -2267,13 +2380,16 @@ CODE
end
t = RBA::Timer::new
t.start
self._process_events
if @force_gc || Time::now - @time > 0.5
if @force_gc || Time::now - @time > 0.5 || @profile
GC.start # force a garbage collection before the operation to free unused memory
@time = Time::now
end
mem_before = RBA::Timer::memory_size
res = yield
mem_after = RBA::Timer::memory_size
t.stop
begin
@ -2283,15 +2399,25 @@ CODE
# Report result statistics
_result_info(res, 1)
mem = RBA::Timer::memory_size
if mem > 0
info("Elapsed: #{'%.3f'%(t.sys+t.user)}s Memory: #{'%.2f'%(mem/(1024*1024))}M", 1)
if mem_after > 0
info("Elapsed: #{'%.3f'%(t.sys+t.user)}s Memory: #{'%.2f'%(mem_after/(1024*1024))}M", 1)
else
info("Elapsed: #{'%.3f'%(t.sys+t.user)}s", 1)
end
end
if @profile
# calls, sys time (in sec), user time (in sec), memory added (in bytes)
info = (@profile_info[desc] ||= [ 0, 0.0, 0.0, 0 ])
info[0] += 1
info[1] += t.sys
info[2] += t.user
info[3] += mem_after - mem_before
end
ensure
# disable progress again
@ -2477,8 +2603,8 @@ CODE
end
def _output_layout
if @output_layout
output = @output_layout
if @def_output && @def_output.layout
output = @def_output.layout
else
output = @def_layout
output || raise("No output layout specified")
@ -2487,19 +2613,79 @@ CODE
end
def _output_cell
if @output_layout
if @output_cell
output_cell = @output_cell
if @def_output && @def_output.layout
if @def_output.cell
output_cell = @def_output.cell
elsif @def_cell
output_cell = @output_layout.cell(@def_cell.name) || @output_layout.create_cell(@def_cell.name)
output_layout = @def_output.layout
output_cell = output_layout.cell(@def_cell.name) || output_layout.create_cell(@def_cell.name)
end
output_cell || raise("No output cell specified (see 'target' instruction)")
else
output_cell = @output_cell || @def_cell
output_cell = @def_cell
output_cell || raise("No output cell specified")
end
output_cell
end
def _dump_profile
if @profile_info.empty?
return
end
desc_title = "Operation"
calls_title = "# calls"
time_title = "Time (s)"
memory_title = "Memory (k)"
titles = [ desc_title, calls_title, time_title, memory_title ]
max_len_desc = [ @profile_info.keys.collect { |s| s.size }.max, desc_title.size ].max
max_len_calls = [ @profile_info.values.collect { |v| v[0].to_s.size }.max, calls_title.size ].max
max_len_time = [ @profile_info.values.collect { |v| ("%.3f" % (v[1] + v[2])).to_s.size }.max, time_title.size ].max
max_len_mem = [ @profile_info.values.collect { |v| v[3].to_s.size }.max, memory_title.size ].max
fmt = "%-" + max_len_desc.to_s + "s " +
"%-" + max_len_calls.to_s + "d " +
"%-" + max_len_time.to_s + ".3f " +
"%-" + max_len_mem.to_s + ".0f"
fmt_title = "%-" + max_len_desc.to_s + "s " +
"%-" + max_len_calls.to_s + "s " +
"%-" + max_len_time.to_s + "s " +
"%-" + max_len_mem.to_s + "s"
pi = @profile_info.keys.collect { |s| [s] + @profile_info[s] }.collect do |desc,calls,sys_time,user_time,memory|
[ desc, calls, sys_time + user_time, memory.to_f / 1024.0 ]
end
self.log("")
self.log("Operations by execution time\n")
self.log(fmt_title % titles)
n = 1
pi.sort { |a,b| b[2] <=> a[2] }.each do |info|
self.log(fmt % info)
n += 1
if @profile_n > 0 && n > @profile_n
self.log("... (%d entries skipped)" % (pi.size - @profile_n))
break
end
end
self.log("")
self.log("Operations by memory adder\n")
self.log(fmt_title % titles)
n = 1
pi.sort { |a,b| b[3] <=> a[3] }.each do |info|
self.log(fmt % info)
n += 1
if @profile_n > 0 && n > @profile_n
self.log("... (%d entries skipped)" % (pi.size - @profile_n))
break
end
end
end
def _start(job_description)
@ -2528,39 +2714,32 @@ CODE
begin
_flush
view = RBA::LayoutView::current
# save the report database if requested
if @output_rdb_file && final
rdb_file = _make_path(@output_rdb_file)
info("Writing report database: #{rdb_file} ..")
@output_rdb.save(rdb_file)
@def_output && @def_output.finish(final, view)
if final
@other_outputs.each do |output|
output.finish(final, view)
end
end
if @output_rdb && final && view
view.show_rdb(@output_rdb_index, view.active_cellview_index)
end
# save the output file if requested
if @output_layout && @output_layout_file
opt = RBA::SaveLayoutOptions::new
gzip = opt.set_format_from_filename(@output_layout_file)
info("Writing layout file: #{@output_layout_file} ..")
@output_layout.write(@output_layout_file, gzip, opt)
# dump the profile information
if @profile && final
_dump_profile
end
# create the new layers as visual layers if necessary
if view
output = @output_layout || @def_layout
output = ( @def_output && @def_output.layout ) || @def_layout
cv_index = nil
view.cellviews.times do |cvi|
view.cellview(cvi).layout == output && cv_index = cvi
end
if cv_index
view = RBA::LayoutView::current
# clear selection
view.cancel
@ -2634,13 +2813,10 @@ CODE
ensure
@output_layers = []
@output_layout = nil
@output_layout_file = nil
@output_cell = nil
@output_rdb_file = nil
@output_rdb_cell = nil
@output_rdb = nil
@output_rdb_index = nil
@def_output = nil
if final
@other_outputs = []
end
@show_l2ndb = nil
@output_l2ndb_file = nil
@ -2933,21 +3109,36 @@ CODE
def _output(data, *args)
if @output_rdb
channel = args.find { |a| a.is_a?(OutputChannel) }
if ! channel
if ! @def_output
@def_output = LayoutOutputChannel::new(self, self._output_layout, self._output_cell, nil)
end
channel = @def_output
end
args = args.select { |a| !a.is_a?(OutputChannel) }
if channel.rdb
if args.size < 1
raise("Invalid number of arguments - category name and optional description expected")
end
cat = @output_rdb.create_category(args[0].to_s)
output_rdb = channel.rdb
output_cell = channel.cell
cat = output_rdb.create_category(args[0].to_s)
args[1] && cat.description = args[1]
cat.scan_collection(@output_rdb_cell, RBA::CplxTrans::new(self.dbu), data)
cat.scan_collection(output_cell, RBA::CplxTrans::new(self.dbu), data)
else
end
output = self._output_layout
output_cell = self._output_cell
if channel.layout
output = channel.layout
output_cell = channel.cell
info = nil
if args.size == 1
@ -2977,14 +3168,16 @@ CODE
begin
if !@used_output_layers[li]
@output_layers.push(li)
# Note: to avoid issues with output onto the input layer, we
# output to a temp layer and later swap both. The simple implementation
# did a clear here and the effect of that was that the data potentially
# got invalidated.
tmp = output.insert_layer(RBA::LayerInfo::new)
@used_output_layers[li] = true
if channel == @def_output
if !@used_output_layers[li]
@output_layers.push(li)
# Note: to avoid issues with output onto the input layer, we
# output to a temp layer and later swap both. The simple implementation
# did a clear here and the effect of that was that the data potentially
# got invalidated.
tmp = output.insert_layer(RBA::LayerInfo::new)
@used_output_layers[li] = true
end
end
# insert the data into the output layer
@ -3020,7 +3213,111 @@ CODE
@layout_sources[name] = src
src
end
def _make_report(description, filename, cellname)
output_rdb_file = filename
name = filename && File::basename(filename)
name ||= "DRC"
output_rdb_index = nil
view = RBA::LayoutView::current
if view
if self._rdb_index
output_rdb = RBA::ReportDatabase::new("") # reuse existing name
output_rdb_index = view.replace_rdb(self._rdb_index, output_rdb)
else
output_rdb = RBA::ReportDatabase::new(name)
output_rdb_index = view.add_rdb(output_rdb)
end
else
output_rdb = RBA::ReportDatabase::new(name)
end
cn = cellname && cellname.to_s
cn ||= @def_cell && @def_cell.name
cn ||= source && source.cell_name
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.generator = self._generator
output_rdb.top_cell_name = cn
output_rdb.description = description
RDBOutputChannel::new(self, output_rdb, output_rdb_index, cn, output_rdb_file)
end
def _make_target(arg, cellname = nil)
if arg.is_a?(String)
if arg =~ /^@(\d+|\+)/
view = RBA::LayoutView::current
view || raise("No view open")
if $1 == "+"
n = view.create_layout(true)
cellname ||= (@def_cell ? @def_cell.name : "TOP")
else
n = $1.to_i - 1
end
(n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}")
cv = view.cellview(n)
cv.is_valid? || raise("Invalid layout @#{n + 1}")
output_layout = cv.layout
output_cell = cellname ? (output_layout.cell(cellname.to_s) || output_layout.create_cell(cellname.to_s)) : cv.cell
cv.cell = output_cell
output_layout_file = nil
else
cn = cellname && cellname.to_s
cn ||= @def_cell && @def_cell.name
cn ||= @def_source && @def_source.cell_obj && @def_source.cell_obj.name
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_layout = RBA::Layout::new
output_cell = output_layout.create_cell(cn)
output_layout_file = arg
end
elsif arg.is_a?(RBA::Layout)
output_layout = arg
output_cell = cellname && (output_layout.cell(cellname.to_s) || output_layout.create_cell(cellname.to_s))
if ! output_cell
begin
output_cell = output_layout.top_cell
rescue
raise("No cell name specified and the layout does not have a unique top cell - specify the name of the top cell to write the output to")
end
end
output_layout_file = nil
elsif arg.is_a?(RBA::Cell)
output_layout = arg.layout
output_cell = arg
output_layout_file = nil
else
raise("Invalid argument '" + arg.inspect + "'")
end
output = LayoutOutputChannel::new(self, output_layout, output_cell, output_layout_file)
end
end
end

View File

@ -5007,6 +5007,26 @@ CODE
#
# See \global#report and \global#target on how to configure output to a target layout
# or report database.
#
# See also \global#new_target and \global#new_report on how to create additional
# targets for output. This allows saving certain layers to different files than
# the standard target or report. To do so, create a new target or report using one
# of these functions and pass that object to the corresponding "output" call as
# an additional argument.
#
# Example:
#
# @code
# check1 = ...
# check2 = ...
# check3 = ...
#
# second_report = new_report("Only for check2", "check2.lyrdb")
#
# check1.output("Check 1")
# check2.output("Check 2", second_report)
# check3.output("Check 3")
# @/code
def output(*args)
@engine._context("output") do

View File

@ -731,6 +731,75 @@ TEST(14_SwitchingTargets)
db::compare_layouts (_this, layout2, au2, db::NoNormalization);
}
TEST(14b_SideTargetsAndReports)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_14b.drc";
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_14b.gds";
std::string au = tl::testdata ();
au += "/drc/drcSimpleTests_au14b.gds";
std::string au2 = tl::testdata ();
au2 += "/drc/drcSimpleTests_au14b_2.gds";
std::string au_report = tl::testdata ();
au_report += "/drc/drcSimpleTests_au14b.lyrdb";
std::string au_report2 = tl::testdata ();
au_report2 += "/drc/drcSimpleTests_au14b_2.lyrdb";
std::string output = this->tmp_file ("tmp.gds");
std::string output2 = this->tmp_file ("tmp2.gds");
std::string report = this->tmp_file ("tmp.lydrc");
std::string report2 = this->tmp_file ("tmp2.lydrc");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_force_gc = true\n"
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
"$drc_test_target2 = '%s'\n"
"$drc_test_report = '%s'\n"
"$drc_test_report2 = '%s'\n"
, input, output, output2, report, report2)
);
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);
db::Layout layout2;
{
tl::InputStream stream (output2);
db::Reader reader (stream);
reader.read (layout2);
}
db::compare_layouts (_this, layout2, au2, db::NoNormalization);
compare_text_files (report, au_report);
compare_text_files (report2, au_report2);
}
TEST(15_issue548)
{
std::string rs = tl::testdata ();

View File

@ -118,6 +118,10 @@ std::string escape_xml_with_formatting (const std::string &s, bool &in_code)
r += "<u>";
} else if (sc.test ("@/u")) {
r += "</u>";
} else if (sc.test ("@tt")) {
r += "<tt>";
} else if (sc.test ("@/tt")) {
r += "</tt>";
} else if (sc.test ("@i")) {
r += "<i>";
} else if (sc.test ("@/i")) {

View File

@ -127,11 +127,19 @@ GDS2Reader::get_record ()
return rec_id;
}
void
GDS2Reader::record_underflow_error ()
{
error (tl::to_string (tr ("Record too short")));
}
inline int
GDS2Reader::get_int ()
{
unsigned char *b = mp_rec_buf + m_recptr;
m_recptr += 4;
if ((m_recptr += 4) > m_reclen) {
record_underflow_error ();
}
int32_t l = *((int32_t *)b);
gds2h (l);
@ -142,7 +150,9 @@ inline short
GDS2Reader::get_short ()
{
unsigned char *b = mp_rec_buf + m_recptr;
m_recptr += 2;
if ((m_recptr += 2) > m_reclen) {
record_underflow_error ();
}
int16_t s = *((int16_t *)b);
gds2h (s);
@ -153,7 +163,9 @@ inline unsigned short
GDS2Reader::get_ushort ()
{
unsigned char *b = mp_rec_buf + m_recptr;
m_recptr += 2;
if ((m_recptr += 2) > m_reclen) {
record_underflow_error ();
}
uint16_t s = *((uint16_t *)b);
gds2h ((int16_t &) s);
@ -164,7 +176,9 @@ inline double
GDS2Reader::get_double ()
{
unsigned char *b = mp_rec_buf + m_recptr;
m_recptr += 8;
if ((m_recptr += 8) > m_reclen) {
record_underflow_error ();
}
uint32_t l0 = ((uint32_t *)b) [0];
gds2h ((int32_t &) l0);

View File

@ -106,6 +106,8 @@ private:
virtual void get_time (unsigned int *mod_time, unsigned int *access_time);
virtual GDS2XY *get_xy_data (unsigned int &length);
virtual void progress_checkpoint ();
void record_underflow_error ();
};
}

View File

@ -278,6 +278,10 @@ GDS2ReaderBase::do_read (db::Layout &layout)
get_string (m_cellname);
if (m_cellname.empty ()) {
error (tl::to_string (tr ("Empty cell name")));
}
// if the first cell is the dummy cell containing the context information
// read this cell in a special way and store the context information separately.
if (first_cell && m_cellname == "$$$CONTEXT_INFO$$$") {

View File

@ -1,76 +1,73 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DiffToolDialog</class>
<widget class="QDialog" name="DiffToolDialog" >
<property name="geometry" >
<widget class="QDialog" name="DiffToolDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>498</width>
<width>503</width>
<height>404</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Diff Tool</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>9</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="title" >
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Input</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="margin" stdset="0">
<number>9</number>
</property>
<property name="spacing" >
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Layout A </string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Layout B</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="lay::CellViewSelectionComboBox" name="layouta" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<item row="0" column="1">
<widget class="lay::CellViewSelectionComboBox" name="layouta">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy" >
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="lay::CellViewSelectionComboBox" name="layoutb" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<item row="1" column="1">
<widget class="lay::CellViewSelectionComboBox" name="layoutb">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy" >
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
</widget>
@ -79,77 +76,84 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2" >
<property name="title" >
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Options</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" stdset="0">
<number>9</number>
</property>
<item>
<widget class="QCheckBox" name="smart_cbx" >
<property name="text" >
<widget class="QCheckBox" name="smart_cbx">
<property name="text">
<string>Don't use names to match cells (use geometrical properties)</string>
</property>
<property name="checked" >
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="xor_cbx" >
<property name="text" >
<widget class="QCheckBox" name="xor_cbx">
<property name="text">
<string>Run XOR on differences</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="summarize_cbx" >
<property name="text" >
<widget class="QCheckBox" name="summarize_cbx">
<property name="text">
<string>Summarize missing layers</string>
</property>
<property name="checked" >
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="detailed_cbx" >
<property name="text" >
<widget class="QCheckBox" name="detailed_cbx">
<property name="text">
<string>Detailed information</string>
</property>
<property name="checked" >
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="expand_cell_arrays_cbx" >
<property name="text" >
<widget class="QCheckBox" name="expand_cell_arrays_cbx">
<property name="text">
<string>Expand cell arrays (compare single instance by instance)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="exact_cbx" >
<property name="text" >
<widget class="QCheckBox" name="exact_cbx">
<property name="text">
<string>Exact compare (includes properties, text orientation and similar)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ignore_duplicates_cbx">
<property name="text">
<string>Ignore duplicate instances and shapes</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<property name="sizeHint" stdset="0">
<size>
<width>472</width>
<height>16</height>
@ -158,12 +162,12 @@
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" >
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@ -187,11 +191,11 @@
<receiver>DiffToolDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
@ -203,11 +207,11 @@
<receiver>DiffToolDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>

View File

@ -46,6 +46,7 @@ std::string cfg_diff_smart ("diff-smart");
std::string cfg_diff_summarize ("diff-summarize");
std::string cfg_diff_expand_cell_arrays ("diff-expand-cell-arrays");
std::string cfg_diff_exact ("diff-exact");
std::string cfg_diff_ignore_duplicates ("diff-ignore-duplicates");
// ------------------------------------------------------------------------------
// RdbDifferenceReceiver definition
@ -650,6 +651,9 @@ DiffToolDialog::exec_dialog (lay::LayoutViewBase *view)
if (config_root->config_get (cfg_diff_exact, f)) {
mp_ui->exact_cbx->setChecked (f);
}
if (config_root->config_get (cfg_diff_ignore_duplicates, f)) {
mp_ui->ignore_duplicates_cbx->setChecked (f);
}
update ();
@ -686,6 +690,7 @@ BEGIN_PROTECTED
config_root->config_set (cfg_diff_summarize, mp_ui->summarize_cbx->isChecked ());
config_root->config_set (cfg_diff_expand_cell_arrays, mp_ui->expand_cell_arrays_cbx->isChecked ());
config_root->config_set (cfg_diff_exact, mp_ui->exact_cbx->isChecked ());
config_root->config_set (cfg_diff_ignore_duplicates, mp_ui->ignore_duplicates_cbx->isChecked ());
config_root->config_end ();
QDialog::accept ();
@ -712,6 +717,7 @@ DiffToolDialog::run_diff ()
bool summarize = !run_xor && mp_ui->summarize_cbx->isChecked ();
bool expand_cell_arrays = !run_xor && mp_ui->expand_cell_arrays_cbx->isChecked ();
bool exact = !run_xor && mp_ui->exact_cbx->isChecked ();
bool ignore_duplicates = mp_ui->ignore_duplicates_cbx->isChecked ();
int cv_index_a = mp_ui->layouta->current_cv_index ();
int cv_index_b = mp_ui->layoutb->current_cv_index ();
@ -740,6 +746,9 @@ DiffToolDialog::run_diff ()
if (smart) {
flags |= db::layout_diff::f_smart_cell_mapping;
}
if (ignore_duplicates) {
flags |= db::layout_diff::f_ignore_duplicates;
}
// TODO: make an parameter
db::Coord tolerance = 0;

View File

@ -34,6 +34,7 @@
#include "gsiDecl.h"
#include "gsiDeclBasic.h"
#include "tlLog.h"
#include "tlEnv.h"
#include "tlStream.h"
#include "tlTimer.h"
#include "tlFileUtils.h"
@ -140,6 +141,16 @@ public:
virtual size_t scope_index () const
{
static int consider_scope = -1;
// disable scoped debugging (e.g. DRC script lines) if $KLAYOUT_PYA_DEBUG_SCOPE is set.
if (consider_scope < 0) {
consider_scope = tl::app_flag ("pya-debug-scope") ? 0 : 1;
}
if (! consider_scope) {
return 0;
}
if (! m_scope.empty ()) {
for (size_t i = 0; i < m_stack_trace.size (); ++i) {
if (m_stack_trace [i].file == m_scope) {

View File

@ -111,7 +111,7 @@ RubyStackTraceProvider::scope_index (const std::vector<tl::BacktraceElement> &bt
// disable scoped debugging (e.g. DRC script lines) if $KLAYOUT_RBA_DEBUG_SCOPE is set.
if (consider_scope < 0) {
consider_scope = tl::has_env ("KLAYOUT_RBA_DEBUG_SCOPE") ? 0 : 1;
consider_scope = tl::app_flag ("rba-debug-scope") ? 0 : 1;
}
if (! consider_scope) {
return 0;

BIN
testdata/algo/hlp18.oas vendored Normal file

Binary file not shown.

24
testdata/drc/drcSimpleTests_14b.drc vendored Normal file
View File

@ -0,0 +1,24 @@
source($drc_test_source)
target($drc_test_target)
l1 = input(1, 0)
l2 = input(2, 0)
tcopy = new_target($drc_test_target2)
rcopy = new_report("Report 2", $drc_test_report2)
l1.output(tcopy, 101, 0)
l2.output(tcopy, 102, 0)
l1.output(1, 0)
l1.space(1.0.um).output(100, 0)
report("Report 1", $drc_test_report)
l2.space(1.0.um).output("l2 space < 1µm")
l1.width(1.0.um).output("l1 width < 1µm")
l1.sep(l2, 1.0.um).output(rcopy, "l1 sep l2 < 1µm")

BIN
testdata/drc/drcSimpleTests_14b.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au14b.gds vendored Normal file

Binary file not shown.

88
testdata/drc/drcSimpleTests_au14b.lyrdb vendored Normal file
View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<report-database>
<description>Report 1</description>
<original-file/>
<generator>drc: script='.drc'</generator>
<top-cell>TOP</top-cell>
<tags>
</tags>
<categories>
<category>
<name>l2 space &lt; 1µm</name>
<description/>
<categories>
</categories>
</category>
<category>
<name>l1 width &lt; 1µm</name>
<description/>
<categories>
</categories>
</category>
</categories>
<cells>
<cell>
<name>TOP</name>
<variant/>
<references>
</references>
</cell>
</cells>
<items>
<item>
<tags/>
<category>'l2 space &lt; 1\302\265m'</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<image/>
<values>
<value>edge-pair: (-0.2,0.7;1,0.7)|(1,1.1;-0.2,1.1)</value>
</values>
</item>
<item>
<tags/>
<category>'l1 width &lt; 1\302\265m'</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<image/>
<values>
<value>edge-pair: (0,0;0,0.9)|(0.3,0.9;0.3,0)</value>
</values>
</item>
<item>
<tags/>
<category>'l1 width &lt; 1\302\265m'</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<image/>
<values>
<value>edge-pair: (0.3,0;0,0)|(0,0.9;0.3,0.9)</value>
</values>
</item>
<item>
<tags/>
<category>'l1 width &lt; 1\302\265m'</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<image/>
<values>
<value>edge-pair: (0.5,0;0.5,0.9)|(0.8,0.9;0.8,0)</value>
</values>
</item>
<item>
<tags/>
<category>'l1 width &lt; 1\302\265m'</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<image/>
<values>
<value>edge-pair: (0.8,0;0.5,0)|(0.5,0.9;0.8,0.9)</value>
</values>
</item>
</items>
</report-database>

BIN
testdata/drc/drcSimpleTests_au14b_2.gds vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<report-database>
<description>Report 2</description>
<original-file/>
<generator>drc: script='.drc'</generator>
<top-cell>TOP</top-cell>
<tags>
</tags>
<categories>
<category>
<name>l1 sep l2 &lt; 1µm</name>
<description/>
<categories>
</categories>
</category>
</categories>
<cells>
<cell>
<name>TOP</name>
<variant/>
<references>
</references>
</cell>
</cells>
<items>
<item>
<tags/>
<category>'l1 sep l2 &lt; 1\302\265m'</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<image/>
<values>
<value>edge-pair: (0,0.9;0.3,0.9)/(1,1.1;-0.2,1.1)</value>
</values>
</item>
<item>
<tags/>
<category>'l1 sep l2 &lt; 1\302\265m'</category>
<cell>TOP</cell>
<visited>false</visited>
<multiplicity>1</multiplicity>
<image/>
<values>
<value>edge-pair: (0.5,0.9;0.8,0.9)/(1,1.1;-0.2,1.1)</value>
</values>
</item>
</items>
</report-database>

View File

@ -41,6 +41,12 @@ class BasicTest(unittest.TestCase):
v.read(os.path.join(os.path.dirname(__file__), "..", "gds", "t10.gds"))
self.assertEqual(v.top_cell().name, "RINGO")
def test_4(self):
# gds2_text plugin loaded? (issue #1393)
v = db.Layout()
v.read(os.path.join(os.path.dirname(__file__), "..", "gds2_txt", "read.txt"))
self.assertEqual(v.top_cell().name, "RINGO")
# run unit tests
if __name__ == '__main__':
suite = unittest.TestSuite()

View File

@ -1971,6 +1971,66 @@ class DBLayoutTests1_TestClass < TestBase
end
def test_23
# layer operations with shape types
m = RBA::Manager::new
l = RBA::Layout.new(m)
l1 = l.insert_layer(RBA::LayerInfo.new(1, 0))
l2 = l.insert_layer(RBA::LayerInfo.new(2, 0))
c0 = l.cell(l.add_cell("c0"))
c1 = l.cell(l.add_cell("c1"))
c0.shapes(l1).insert(RBA::Box::new(1, 2, 3, 4))
c1.shapes(l1).insert(RBA::Polygon::new(RBA::Box::new(1, 2, 3, 4)))
str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)")
m.transaction("T")
l.clear_layer(l1, RBA::Shapes::SPolygons)
m.commit
str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str1, "c0:box (1,2;3,4)\nc1:")
m.undo
str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)")
m.transaction("T")
l.move_layer(l1, l2, RBA::Shapes::SPolygons)
m.commit
str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str1, "c0:box (1,2;3,4)\nc1:")
str2 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l2).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str2, "c0:\nc1:polygon (1,2;1,4;3,4;3,2)")
m.undo
str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)")
str2 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l2).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str2, "c0:\nc1:")
m.transaction("T")
l.copy_layer(l1, l2, RBA::Shapes::SPolygons)
m.commit
str1 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l1).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str1, "c0:box (1,2;3,4)\nc1:polygon (1,2;1,4;3,4;3,2)")
str2 = l.each_cell.collect { |c| c.name + ":" + c.shapes(l2).each.collect { |sh| sh.to_s }.join(";") }.join("\n")
assert_equal(str2, "c0:\nc1:polygon (1,2;1,4;3,4;3,2)")
end
# Iterating while flatten
def test_issue200

View File

@ -1595,6 +1595,34 @@ class DBShapes_TestClass < TestBase
end
# Shapes with shape-type specific insert and clear
def test_11
s = RBA::Shapes::new
s.insert(RBA::Box::new(1, 2, 3, 4))
s.insert(RBA::Polygon::new(RBA::Box::new(1, 2, 3, 4)))
assert_equal(s.each.collect { |sh| sh.to_s }.join("; "), "polygon (1,2;1,4;3,4;3,2); box (1,2;3,4)")
s2 = RBA::Shapes::new
s2.insert(s)
assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "polygon (1,2;1,4;3,4;3,2); box (1,2;3,4)")
s2.clear(RBA::Shapes::SPolygons)
assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "box (1,2;3,4)")
s2.clear
assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "")
s2.insert(s, RBA::Shapes::SPolygons)
assert_equal(s2.each.collect { |sh| sh.to_s }.join("; "), "polygon (1,2;1,4;3,4;3,2)")
end
end
load("test_epilogue.rb")