From 2d0a9418f9622a465bf8ebbd3a676aa43c4a4e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 5 Jun 2020 10:55:07 +0200 Subject: [PATCH 1/4] Implemented #579 (perimeter_only mode for antenna check) (#582) * WIP: added basic feature and tests. * WIP: provide tests are GSI binding of new antenna check * Fixed issue #579 (perimeter_only mode for antenna check) * Updated DRC doc for 'perimeter_only' --- src/db/db/dbLayoutToNetlist.cc | 12 +++++-- src/db/db/dbLayoutToNetlist.h | 23 ++++++++++--- src/db/db/gsiDeclDbLayoutToNetlist.cc | 34 ++++++++++++++++---- src/db/unit_tests/dbLayoutToNetlistTests.cc | 19 ++++++++++- src/drc/drc/built-in-macros/_drc_engine.rb | 8 +++-- src/drc/drc/built-in-macros/_drc_netter.rb | 18 ++++++++++- src/drc/drc/built-in-macros/_drc_tags.rb | 6 ++-- src/lay/lay/doc/about/drc_ref_netter.xml | 12 +++++++ testdata/algo/antenna_au1.gds | Bin 11330 -> 14850 bytes testdata/drc/drcSimpleTests_7.drc | 7 ++++ testdata/drc/drcSimpleTests_au7.gds | Bin 7706 -> 10074 bytes testdata/ruby/dbLayoutToNetlist.rb | 18 +++++++++++ 12 files changed, 138 insertions(+), 19 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 3dbcea43e..9e2d164d5 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -1180,7 +1180,7 @@ db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Poin } } -db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector > &diodes) +db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector > &diodes) { // TODO: that's basically too much .. we only need the clusters if (! m_netlist_extracted) { @@ -1210,12 +1210,18 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_p deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0); deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0); - double agate = rgate.area () * dbu * dbu; + double agate = 0.0; + if (fabs (gate_area_factor) > 1e-6) { + agate += rgate.area () * dbu * dbu * gate_area_factor; + } if (fabs (gate_perimeter_factor) > 1e-6) { agate += rgate.perimeter () * dbu * gate_perimeter_factor; } - double ametal = rmetal.area () * dbu * dbu; + double ametal = 0.0; + if (fabs (metal_area_factor) > 1e-6) { + ametal += rmetal.area () * dbu * dbu * metal_area_factor; + } if (fabs (metal_perimeter_factor) > 1e-6) { ametal += rmetal.perimeter () * dbu * metal_perimeter_factor; } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 13ac9fe7f..63bf835aa 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -733,9 +733,9 @@ public: * The area computation of gate and metal happens by taking the polygon * area (A) and perimeter (P) into account: * - * A(antenna) = A + P * f + * A(antenna) = A + P * t * - * where f is the perimeter factor. The unit of the area factor is + * where t is the perimeter factor. The unit of this area factor is * micrometers. * * The limit ratio can be modified by the presence of connections to @@ -750,7 +750,10 @@ public: * regardless of the diode's area. * In other words: any diode will make the net safe against antenna discharge. */ - db::Region antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector > &diodes = std::vector > ()); + db::Region antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector > &diodes = std::vector > ()) + { + return antenna_check (gate, 1.0, gate_perimeter_factor, metal, 1.0, metal_perimeter_factor, ratio, diodes); + } /** * @brief Variant of the antennna check not using the perimeter @@ -758,9 +761,21 @@ public: */ db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector > &diodes = std::vector > ()) { - return antenna_check (gate, 0.0, metal, 0.0, ratio, diodes); + return antenna_check (gate, 1.0, 0.0, metal, 1.0, 0.0, ratio, diodes); } + /** + * @brief Variant of the antenna check providing an area scale factor + * + * This version provides an additional area scale factor f, so the effective area becomes + * + * A(antenna) = A * f + P * t + * + * where f is the area scale factor and t the perimeter scale factor. This version allows to ignore the + * area contribution entirely and switch to a perimeter-based antenna check by setting f to zero. + */ + db::Region antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector > &diodes = std::vector > ()); + /** * @brief Saves the database to the given path * diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 3c0b85fd0..1e5cce32d 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -98,7 +98,7 @@ static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) return ln; } -static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector &diodes) +static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_area_factor, double poly_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector &diodes) { std::vector > diode_pairs; @@ -127,12 +127,17 @@ static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &po } - return l2n->antenna_check (poly, poly_perimeter_factor, metal, metal_perimeter_factor, ratio, diode_pairs); + return l2n->antenna_check (poly, poly_area_factor, poly_perimeter_factor, metal, metal_area_factor, metal_perimeter_factor, ratio, diode_pairs); +} + +static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector &diodes) +{ + return antenna_check3 (l2n, poly, 1, poly_perimeter_factor, metal, 1, metal_perimeter_factor, ratio, diodes); } static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector &diodes) { - return antenna_check2 (l2n, poly, 0, metal, 0, ratio, diodes); + return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes); } Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", @@ -617,15 +622,32 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "The effective area is computed using:\n" "\n" "@code\n" - "Aeff = A + P * f\n" + "Aeff = A + P * t\n" "@/code\n" "\n" - "Here Aeff is the area used in the check, A is the polygon area, P the perimeter and f the perimeter factor. " - "This formula applies to gate polygon area/perimeter with 'gate_perimeter_factor' for f and metal polygon area/perimeter " + "Here Aeff is the area used in the check, A is the polygon area, P the perimeter and t the perimeter factor. " + "This formula applies to gate polygon area/perimeter with 'gate_perimeter_factor' for t and metal polygon area/perimeter " "with 'metal_perimeter_factor'. The perimeter_factor has the dimension of micrometers and can be thought of as the width " "of the material. Essentially the side walls of the material are taking into account for the surface area as well.\n" "\n" "This variant has been introduced in version 0.26.6.\n" + ) + + gsi::method_ext ("antenna_check", &antenna_check3, gsi::arg ("gate"), gsi::arg ("gate_area_factor"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_area_factor"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector (), "[]"), + "@brief Runs an antenna check on the extracted clusters taking the perimeter into account and providing an area factor\n" + "\n" + "This (most generic) version of the \\antenna_check method allows taking the perimeter of gate or metal into account and also " + "provides a scaling factor for the area part.\n" + "The effective area is computed using:\n" + "\n" + "@code\n" + "Aeff = A * f + P * t\n" + "@/code\n" + "\n" + "Here f is the area factor and t the perimeter factor. A is the polygon area and P the polygon perimeter. " + "A use case for this variant is to set the area factor to zero. This way, only perimeter contributions are " + "considered.\n" + "\n" + "This variant has been introduced in version 0.26.6.\n" ), "@brief A generic framework for extracting netlists from layouts\n" "\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 144edcf1f..3e886b16a 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -2639,8 +2639,16 @@ TEST(10_Antenna) a5_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (500, 0))); a5_15.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (501, 0))); a5_29.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (502, 0))); - } + // with area factor + db::Region b5_5 = l2n.antenna_check (*rpoly, 2.0, 0.0, *rmetal2, 1.0, 1.0, 2.5); + db::Region b5_15 = l2n.antenna_check (*rpoly, 2.0, 0.0, *rmetal2, 1.0, 1.0, 7.5); + db::Region b5_29 = l2n.antenna_check (*rpoly, 2.0, 0.0, *rmetal2, 1.0, 1.0, 14.5); + + b5_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (550, 0))); + b5_15.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (551, 0))); + b5_29.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (552, 0))); + } { db::LayoutToNetlist l2n (&dss); @@ -2672,6 +2680,15 @@ TEST(10_Antenna) a6_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (600, 0))); a6_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (601, 0))); a6_9.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (602, 0))); + + // with area factor + db::Region b6_3 = l2n.antenna_check (*rpoly, 1.0, 0.3, *rmetal2, 2.0, 0.0, 6); + db::Region b6_5 = l2n.antenna_check (*rpoly, 1.0, 0.3, *rmetal2, 2.0, 0.0, 10); + db::Region b6_9 = l2n.antenna_check (*rpoly, 1.0, 0.3, *rmetal2, 2.0, 0.0, 18); + + b6_3.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (650, 0))); + b6_5.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (651, 0))); + b6_9.insert_into (&ly2, top2.cell_index (), ly2.insert_layer (db::LayerProperties (652, 0))); } std::string au = tl::testsrc (); diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 0a185d477..6c6b6186a 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -122,11 +122,15 @@ module DRC end def area_only(r) - DRCAreaAndPerimeter::new(r, 0.0) + DRCAreaAndPerimeter::new(r, 1.0, 0.0) + end + + def perimeter_only(r, f) + DRCAreaAndPerimeter::new(r, 0.0, f) end def area_and_perimeter(r, f) - DRCAreaAndPerimeter::new(r, f) + DRCAreaAndPerimeter::new(r, 1.0, f) end # %DRC% diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 4006c2054..2c32640ad 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -370,6 +370,18 @@ module DRC # errors = antenna_check(gate, ...) # @/code # + # Finally there is also "perimeter_only". When using this + # specification with a thickness value, the area is computed + # from the perimeter alone: + # + # @code + # A(eff) = P * t + # @/code + # + # @code + # errors = antenna_check(perimeter_only(gate, 0.5), ...) + # @/code + # # The error shapes produced by the antenna check are copies # of the metal shapes on the metal layers of each network # violating the antenna rule. @@ -377,11 +389,13 @@ module DRC def antenna_check(agate, ametal, ratio, *diodes) gate_perimeter_factor = 0.0 + gate_area_factor = 1.0 if agate.is_a?(DRC::DRCLayer) gate = agate elsif agate.is_a?(DRC::DRCAreaAndPerimeter) gate = agate.region gate_perimeter_factor = agate.perimeter_factor + gate_area_factor = agate.area_factor if ! gate.is_a?(DRC::DRCLayer) raise("gate with area or area_and_perimeter: input argument must be a layer") end @@ -392,11 +406,13 @@ module DRC gate.requires_region("Netter#antenna_check (gate argument)") metal_perimeter_factor = 0.0 + metal_area_factor = 1.0 if ametal.is_a?(DRC::DRCLayer) metal = ametal elsif ametal.is_a?(DRC::DRCAreaAndPerimeter) metal = ametal.region metal_perimeter_factor = ametal.perimeter_factor + metal_area_factor = ametal.area_factor if ! metal.is_a?(DRC::DRCLayer) raise("metal with area or area_and_perimeter: input argument must be a layer") end @@ -421,7 +437,7 @@ module DRC end end - DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_perimeter_factor, metal.data, metal_perimeter_factor, ratio, dl)) + DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl)) end diff --git a/src/drc/drc/built-in-macros/_drc_tags.rb b/src/drc/drc/built-in-macros/_drc_tags.rb index 9584adcf4..4cbe21533 100644 --- a/src/drc/drc/built-in-macros/_drc_tags.rb +++ b/src/drc/drc/built-in-macros/_drc_tags.rb @@ -117,10 +117,12 @@ module DRC # optional perimeter factor class DRCAreaAndPerimeter attr_accessor :region + attr_accessor :area_factor attr_accessor :perimeter_factor - def initialize(r, f) + def initialize(r, f, t) self.region = r - self.perimeter_factor = f + self.area_factor = f + self.perimeter_factor = t end end diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml index 39eb2a6a0..6d4a91380 100644 --- a/src/lay/lay/doc/about/drc_ref_netter.xml +++ b/src/lay/lay/doc/about/drc_ref_netter.xml @@ -167,6 +167,18 @@ errors = antenna_check(area_and_perimeter(gate, 0.0), ...) errors = antenna_check(gate, ...)

+Finally there is also "perimeter_only". When using this +specification with a thickness value, the area is computed +from the perimeter alone: +

+

+A(eff) = P * t
+
+

+

+errors = antenna_check(perimeter_only(gate, 0.5), ...)
+
+

The error shapes produced by the antenna check are copies of the metal shapes on the metal layers of each network violating the antenna rule. diff --git a/testdata/algo/antenna_au1.gds b/testdata/algo/antenna_au1.gds index 9c56d0a66d5240862eb0eb9382ecc46b4daba1b6..7527f097c95b37f34b811cc1a2c5cfed85d995c7 100644 GIT binary patch delta 460 zcmZvWF>1p=5JhJxMKu!R~Cf&*C@jB8af7Vw-;rpWj{c%~+1AOFeB5_Mn?n*UE+2|)T8&Dw&3j+Xtj2TD( diff --git a/testdata/drc/drcSimpleTests_7.drc b/testdata/drc/drcSimpleTests_7.drc index 8ed2a0e36..28797e54b 100644 --- a/testdata/drc/drcSimpleTests_7.drc +++ b/testdata/drc/drcSimpleTests_7.drc @@ -52,3 +52,10 @@ antenna_check(area_and_perimeter(gate, 70.nm), area_only(metal2), 5.0, diode).ou antenna_check(area_and_perimeter(gate, 0.07), area_only(metal2), 10.0, diode).output(410) antenna_check(area_and_perimeter(gate, 0.07), area_only(metal2), 50.0, diode).output(450) +antenna_check(perimeter_only(gate, 0.07.um), area_only(metal2), 1.0, diode).output(501) +antenna_check(perimeter_only(gate, 0.07.um), area_only(metal2), 1.5, diode).output(502) +antenna_check(perimeter_only(gate, 0.07.um), area_only(metal2), 2.0, diode).output(503) +antenna_check(perimeter_only(gate, 70.nm), area_only(metal2), 5.0, diode).output(505) +antenna_check(perimeter_only(gate, 0.07), area_only(metal2), 10.0, diode).output(510) +antenna_check(perimeter_only(gate, 0.07), area_only(metal2), 50.0, diode).output(550) + diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds index 2a6e918c27cf807704c3c992eb646f4c90b59d0b..cb53332ae815512be018c2b37a2e981ae13eb72d 100644 GIT binary patch delta 365 zcmZvUF$%&!6a{~lxCw$71-rG0$50GjAcb03}f~52c?vf_>_SqOn zGdyPZ&u#b0Q_-T-MNFWgH+CqAY-O;kbd!xf$W-g|@sOWH!brqS250}ds(h|S6OU_f zH*qYYXDayACdt5d?%Dh{X%^lTWJ9*3S<;gYXj!l&jWY(UK{EDQir$P#}5 diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index a6e3b6922..094eef598 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -798,6 +798,15 @@ END assert_equal((a5_15.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(501, 0)))).to_s, "") assert_equal((a5_29.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(502, 0)))).to_s, "") + b5_5 = l2n.antenna_check(rpoly, 2.0, 0.0, rmetal2, 1.0, 1.0, 2.5) + b5_15 = l2n.antenna_check(rpoly, 2.0, 0.0, rmetal2, 1.0, 1.0, 7.5) + b5_29 = l2n.antenna_check(rpoly, 2.0, 0.0, rmetal2, 1.0, 1.0, 14.5) + + # Note: flatten.merged performs some normalization + assert_equal((b5_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(500, 0)))).to_s, "") + assert_equal((b5_15.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(501, 0)))).to_s, "") + assert_equal((b5_29.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(502, 0)))).to_s, "") + # --- antenna check gate perimeter included l2n._destroy @@ -830,6 +839,15 @@ END assert_equal((a6_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(601, 0)))).to_s, "") assert_equal((a6_9.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(602, 0)))).to_s, "") + b6_3 = l2n.antenna_check(rpoly, 1.0, 0.3, rmetal2, 2.0, 0.0, 6) + b6_5 = l2n.antenna_check(rpoly, 1.0, 0.3, rmetal2, 2.0, 0.0, 10) + b6_9 = l2n.antenna_check(rpoly, 1.0, 0.3, rmetal2, 2.0, 0.0, 18) + + # Note: flatten.merged performs some normalization + assert_equal((b6_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(600, 0)))).to_s, "") + assert_equal((b6_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(601, 0)))).to_s, "") + assert_equal((b6_9.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(602, 0)))).to_s, "") + end end From 8db1e3577f5daeca0bca25a086cfe90fe3f91ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 5 Jun 2020 10:57:14 +0200 Subject: [PATCH 2/4] Fixed issue #578 (CIF writer issue with arbitrary angle) (#581) * Fixed isse #578 (CIF writer issue with arbitrary angle) * Added missing files. --- .../streamers/cif/db_plugin/dbCIFWriter.cc | 27 +++++++----------- .../streamers/cif/unit_tests/dbCIFReader.cc | 6 ++++ testdata/cif/issue_578.cif | 18 ++++++++++++ testdata/cif/issue_578_au.gds | Bin 0 -> 668 bytes 4 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 testdata/cif/issue_578.cif create mode 100644 testdata/cif/issue_578_au.gds diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc index 955eb8569..1b1a98459 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc +++ b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc @@ -165,23 +165,18 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa } double a = t.angle(); - while (a < 0) { - a += 360.0; - } - double ya = 0.0, xa = 0.0; - if (a < 45 || a > 315) { - xa = 1.0; - ya = tan(a / 180.0 * M_PI); - } else if (a < 135) { - xa = 1.0 / tan(a / 180.0 * M_PI); - ya = 1.0; - } else if (a < 225) { - xa = -1.0; - ya = tan(a / 180.0 * M_PI); + double xa = cos(a / 180.0 * M_PI); + double ya = sin(a / 180.0 * M_PI); + + // normalize xa or ya whichever is better + double n; + if (fabs (xa) >= M_SQRT1_2) { + n = 1.0 / fabs (xa); } else { - xa = 1.0 / tan(a / 180.0 * M_PI); - ya = -1.0; - } + n = 1.0 / fabs (ya); + } + xa *= n; + ya *= n; // TODO: that can be done smarter ... for (int n = 0; n < 20 && (fabs (xa - floor (0.5 + xa)) > 1e-3 || fabs (ya - floor (0.5 + ya)) > 1e-3); ++n) { diff --git a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc index 8f1cc2b3a..7b74f0846 100644 --- a/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc +++ b/src/plugins/streamers/cif/unit_tests/dbCIFReader.cc @@ -200,3 +200,9 @@ TEST(rot_instances) { run_test (_this, tl::testsrc (), "issue_568.cif", "issue_568_au.gds"); } + +// Issue #578 +TEST(rot_instances2) +{ + run_test (_this, tl::testsrc (), "issue_578.cif", "issue_578_au.gds"); +} diff --git a/testdata/cif/issue_578.cif b/testdata/cif/issue_578.cif new file mode 100644 index 000000000..54695dcd8 --- /dev/null +++ b/testdata/cif/issue_578.cif @@ -0,0 +1,18 @@ +(CIF file written 2020-06-04 16:17:46 by KLayout); +DS 1 1 10; +9 F; +L L1D0; +P 0,0 0,7000 4000,7000 4000,6000 1000,6000 1000,4000 2000,4000 2000,3000 1000,3000 1000,0; +DF; +DS 2 1 10; +9 ALL; +C1 R1,0 T0,0; +C1 R1,1 T0,0; +C1 R0,1 T0,0; +C1 R-1,-1 T0,0; +C1 R-1,0 T0,0; +C1 R-1,1 T0,0; +C1 R0,-1 T0,0; +C1 R1,-1 T0,0; +DF; +E diff --git a/testdata/cif/issue_578_au.gds b/testdata/cif/issue_578_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..aacd0f06b866161b457a818ef2b6b61aab607ae9 GIT binary patch literal 668 zcma)3Jqp4w7=2BXuSKiY9}qNG5EnsQ#8wf+jt(Lo!dr-EaB*{WaPS1q9>KxE(a}v) zYoH3+eu2C|-d_j^j7=d8W?ewwaMfzX&#)N Date: Fri, 5 Jun 2020 10:58:53 +0200 Subject: [PATCH 3/4] Implemented #560 (multiple technologies on libraries) (#576) * First implementation. * PORT BACK: fixed a few flaws (fixed-width side panel ..) 1. On "save as" the filename displayed in the cell view selection box was not updated 2. The width of the library and cellview panel could not be reduced below the width of the combo boxes in the headers. So the panels might have become pretty wide without being able to reduce them. * Implemented #560 (multiple techs on libraries) --- src/db/db/dbLibrary.cc | 33 ++ src/db/db/dbLibrary.h | 37 +- src/db/db/gsiDeclDbLibrary.cc | 45 +- src/edt/edt/EditorOptionsInst.ui | 386 +++++++++--------- src/edt/edt/InstPropertiesPage.ui | 108 ++++- src/lay/lay/layLibraryController.cc | 2 +- src/lay/lay/layTechSetupDialog.cc | 2 +- .../laybasic/layHierarchyControlPanel.cc | 1 + src/laybasic/laybasic/layLayoutView.cc | 2 + src/laybasic/laybasic/layLibrariesView.cc | 8 +- src/laybasic/laybasic/layWidgets.cc | 7 +- src/rba/unit_tests/rba.cc | 1 + testdata/ruby/dbLibrary.rb | 93 +++++ 13 files changed, 499 insertions(+), 226 deletions(-) create mode 100644 testdata/ruby/dbLibrary.rb diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index fec55c29b..6c0ca490c 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -46,6 +46,39 @@ Library::~Library () // .. nothing yet .. } +bool +Library::is_for_technology (const std::string &name) const +{ + return m_technologies.find (name) != m_technologies.end (); +} + +bool +Library::for_technologies () const +{ + return ! m_technologies.empty (); +} + +void +Library::set_technology (const std::string &t) +{ + m_technologies.clear (); + if (! t.empty ()) { + m_technologies.insert (t); + } +} + +void +Library::clear_technologies () +{ + m_technologies.clear (); +} + +void +Library::add_technology (const std::string &tech) +{ + m_technologies.insert (tech); +} + void Library::register_proxy (db::LibraryProxy *lib_proxy, db::Layout *ly) { diff --git a/src/db/db/dbLibrary.h b/src/db/db/dbLibrary.h index 422eec281..21cd70061 100644 --- a/src/db/db/dbLibrary.h +++ b/src/db/db/dbLibrary.h @@ -31,6 +31,7 @@ #include "tlObject.h" #include +#include namespace db { @@ -108,18 +109,38 @@ public: * If this attribute is non-empty, the library is selected only when the given technology is * used for the layout. */ - const std::string &get_technology () const + const std::set &get_technologies () const { - return m_technology; + return m_technologies; } /** - * @brief Sets the technology name this library is associated with + * @brief Gets a value indicating whether this library is associated with the given technology */ - void set_technology (const std::string &t) - { - m_technology = t; - } + bool is_for_technology (const std::string &name) const; + + /** + * @brief Gets a value indicating whether the library is associated with any technology + */ + bool for_technologies () const; + + /** + * @brief Sets the technology name this library is associated with + * + * This will reset the list of technologies to this one. + * If the given technology string is empty, the list of technologies will be cleared. + */ + void set_technology (const std::string &t); + + /** + * @brief Clears the list of technologies this library is associated with + */ + void clear_technologies (); + + /** + * @brief Additionally associate the library with the given technology + */ + void add_technology (const std::string &tech); /** * @brief Getter for the description property @@ -198,7 +219,7 @@ public: private: std::string m_name; std::string m_description; - std::string m_technology; + std::set m_technologies; lib_id_type m_id; db::Layout m_layout; std::map m_referrers; diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index fff5bea12..98f737d88 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -68,6 +68,16 @@ static void delete_lib (db::Library *lib) db::LibraryManager::instance ().delete_lib (lib); } +static std::string get_technology (db::Library *lib) +{ + const std::set &techs = lib->get_technologies (); + if (techs.empty ()) { + return std::string (); + } else { + return *techs.begin (); + } +} + Class decl_Library ("db", "Library", gsi::constructor ("new", &new_lib, "@brief Creates a new, empty library" @@ -111,18 +121,47 @@ Class decl_Library ("db", "Library", gsi::method ("description=", &db::Library::set_description, gsi::arg ("description"), "@brief Sets the libraries' description text\n" ) + - gsi::method ("technology", &db::Library::get_technology, + gsi::method_ext ("#technology", &get_technology, "@brief Returns name of the technology the library is associated with\n" "If this attribute is a non-empty string, this library is only offered for " "selection if the current layout uses this technology.\n" "\n" - "This attribute has been introduced in version 0.25." + "This attribute has been introduced in version 0.25. In version 0.27 this attribute is deprecated as " + "a library can now be associated with multiple technologies." ) + gsi::method ("technology=", &db::Library::set_technology, gsi::arg ("technology"), "@brief sets the name of the technology the library is associated with\n" "\n" "See \\technology for details. " - "This attribute has been introduced in version 0.25." + "This attribute has been introduced in version 0.25. In version 0.27, a library can be associated with " + "multiple technologies and this method will revert the selection to a single one. Passing an empty string " + "is equivalent to \\clear_technologies." + ) + + gsi::method ("clear_technologies", &db::Library::clear_technologies, + "@brief Clears the list of technologies the library is associated with.\n" + "See also \\add_technology.\n" + "\n" + "This method has been introduced in version 0.27" + ) + + gsi::method ("add_technology", &db::Library::add_technology, gsi::arg ("tech"), + "@brief Additionally associates the library with the given technology.\n" + "See also \\clear_technologies.\n" + "\n" + "This method has been introduced in version 0.27" + ) + + gsi::method ("is_for_technology", &db::Library::is_for_technology, gsi::arg ("tech"), + "@brief Returns a value indicating whether the library is associated with the given technology.\n" + "This method has been introduced in version 0.27" + ) + + gsi::method ("for_technologies", &db::Library::for_technologies, + "@brief Returns a value indicating whether the library is associated with any technology.\n" + "The method is equivalent to checking whether the \\technologies list is empty.\n" + "\n" + "This method has been introduced in version 0.27" + ) + + gsi::method ("technologies", &db::Library::get_technologies, + "@brief Gets the list of technologies this library is associated with.\n" + "This method has been introduced in version 0.27" ) + gsi::method ("layout_const", (const db::Layout &(db::Library::*)() const) &db::Library::layout, "@brief The layout object where the cells reside that this library defines (const version)\n" diff --git a/src/edt/edt/EditorOptionsInst.ui b/src/edt/edt/EditorOptionsInst.ui index 74a3eb7be..3cd38120a 100644 --- a/src/edt/edt/EditorOptionsInst.ui +++ b/src/edt/edt/EditorOptionsInst.ui @@ -1,7 +1,8 @@ - + + EditorOptionsInst - - + + 0 0 @@ -9,52 +10,66 @@ 574 - + Form - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - + + QFrame::NoFrame - + QFrame::Raised - - - 0 - - + + 6 + + 0 + + + 0 + + + 0 + + + 0 + - - - - 0 - 0 + + + 0 0 - + Cell - - - - 7 - 0 + + + 0 0 @@ -62,47 +77,41 @@ - - + + ... - - + + Library - - - - - - Qt::Horizontal + + + + 0 + 0 + - - - 40 - 20 - - - + - + Qt::Vertical - + QSizePolicy::Fixed - + 522 8 @@ -111,99 +120,111 @@ - - - - 7 - 7 + + + 0 1 - + 0 - - + + Geometry - - - 9 - - + + 6 + + 9 + + + 9 + + + 9 + + + 9 + - - + + Rotation / Scaling - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - - - 0 - 0 + + + + 0 0 - - - + + + Mirror - + mirror_cbx - - - + + + Scaling factor (magnification) - - - - - 0 - 0 + + + + 0 0 - - - + + + degree - - - + + + Rotation angle - - - + + + (at X-axis before rotation) @@ -212,182 +233,177 @@ - - - - 5 - 5 + + + 0 0 - + Array Instance - + true - - + + 9 - + + 9 + + + 9 + + + 9 + + 6 - - - + + + Column vector (x,y) - - - - - 0 - 0 + + + + 1 0 - - - + + + x = - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 + + + + 1 0 - - - + + + y = - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 + + + + 1 0 - - - + + + y = - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 + + + + 1 0 - - - + + + x = - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - + + + Row vector (x,y) - - - + + + Rows/Columns - - - + + + columns = - - - - - 0 - 0 + + + + 1 0 - - - + + + rows = - - - - - 0 - 0 + + + + 1 0 - - - + + + Warning: although row and column vectors can be arbitrary combination, some design systems only accept orthogonal (rectangular) arrays. - + true @@ -397,10 +413,10 @@ some design systems only accept orthogonal (rectangular) arrays. - + Qt::Vertical - + 20 40 @@ -410,26 +426,26 @@ some design systems only accept orthogonal (rectangular) arrays. - - + + PCell - - + + Place origin of cell - + Qt::Vertical - + 50 8 @@ -462,8 +478,6 @@ some design systems only accept orthogonal (rectangular) arrays. column_y_le place_origin_cb - - - + diff --git a/src/edt/edt/InstPropertiesPage.ui b/src/edt/edt/InstPropertiesPage.ui index 0c6eea4c2..ab2f59622 100644 --- a/src/edt/edt/InstPropertiesPage.ui +++ b/src/edt/edt/InstPropertiesPage.ui @@ -17,7 +17,16 @@ 6 - + + 9 + + + 9 + + + 9 + + 9 @@ -32,7 +41,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -84,7 +102,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -119,20 +146,14 @@ - - - - - - Qt::Horizontal + + + + 0 + 0 + - - - 40 - 20 - - - + @@ -178,7 +199,16 @@ 6 - + + 9 + + + 9 + + + 9 + + 9 @@ -190,7 +220,16 @@ QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0 @@ -265,7 +304,16 @@ Position / Rotation / Scaling - + + 9 + + + 9 + + + 9 + + 9 @@ -393,7 +441,16 @@ true - + + 9 + + + 9 + + + 9 + + 9 @@ -629,7 +686,16 @@ some design systems only accept orthogonal (rectangular) arrays. 6 - + + 0 + + + 0 + + + 0 + + 0 diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index b9b304d37..c76f13667 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -205,7 +205,7 @@ LibraryController::sync_files () } } - tl::log << "Registering as '" << lib->get_name () << "' for tech '" << lib->get_technology () << "'"; + tl::log << "Registering as '" << lib->get_name () << "' for tech '" << p->second << "'"; new_lib_files.insert (std::make_pair (lib_path, std::make_pair (lib->get_name (), fi.lastModified ()))); db::LibraryManager::instance ().register_lib (lib.release ()); diff --git a/src/lay/lay/layTechSetupDialog.cc b/src/lay/lay/layTechSetupDialog.cc index e89396411..266c42cf6 100644 --- a/src/lay/lay/layTechSetupDialog.cc +++ b/src/lay/lay/layTechSetupDialog.cc @@ -135,7 +135,7 @@ TechBaseEditorPage::setup () for (db::LibraryManager::iterator l = db::LibraryManager::instance ().begin (); l != db::LibraryManager::instance ().end (); ++l) { const db::Library *lib = db::LibraryManager::instance ().lib (l->second); - if (lib->get_technology () == tech ()->name ()) { + if (lib->is_for_technology (tech ()->name ())) { std::string text = lib->get_name (); if (! lib->get_description ().empty ()) { text += " - " + lib->get_description (); diff --git a/src/laybasic/laybasic/layHierarchyControlPanel.cc b/src/laybasic/laybasic/layHierarchyControlPanel.cc index d983e5b64..5ca8186dc 100644 --- a/src/laybasic/laybasic/layHierarchyControlPanel.cc +++ b/src/laybasic/laybasic/layHierarchyControlPanel.cc @@ -213,6 +213,7 @@ HierarchyControlPanel::HierarchyControlPanel (lay::LayoutView *view, QWidget *pa mp_selector = new QComboBox (this); mp_selector->setObjectName (QString::fromUtf8 ("cellview_selection")); + mp_selector->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Fixed); ly->addWidget (mp_selector); mp_search_frame = new QFrame (this); diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 099c84e63..44d534acf 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -4739,6 +4739,8 @@ LayoutView::active_library_changed (int /*index*/) void LayoutView::cellview_changed (unsigned int index) { + mp_hierarchy_panel->do_update_content (index); + cellview_changed_event (index); if (m_title.empty ()) { diff --git a/src/laybasic/laybasic/layLibrariesView.cc b/src/laybasic/laybasic/layLibrariesView.cc index 3ef2bb4b6..5209b6f68 100644 --- a/src/laybasic/laybasic/layLibrariesView.cc +++ b/src/laybasic/laybasic/layLibrariesView.cc @@ -210,7 +210,8 @@ LibrariesView::LibrariesView (lay::LayoutView *view, QWidget *parent, const char ly->setContentsMargins (0, 0, 0, 0); mp_selector = new QComboBox (this); - mp_selector->setObjectName (QString::fromUtf8 ("cellview_selection")); + mp_selector->setObjectName (QString::fromUtf8 ("library_selection")); + mp_selector->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Fixed); ly->addWidget (mp_selector); mp_search_frame = new QFrame (this); @@ -771,9 +772,10 @@ LibrariesView::display_string (int n) const if (! lib->get_description ().empty ()) { text += " - " + lib->get_description (); } - if (! lib->get_technology ().empty ()) { + if (lib->for_technologies ()) { text += " "; - text += tl::to_string (QObject::tr ("[Technology %1]").arg (tl::to_qstring (lib->get_technology ()))); + std::string tn = tl::join (std::vector (lib->get_technologies ().begin (), lib->get_technologies ().end ()), ","); + text += tl::to_string (QObject::tr ("[Technology %1]").arg (tl::to_qstring (tn))); } return text; } diff --git a/src/laybasic/laybasic/layWidgets.cc b/src/laybasic/laybasic/layWidgets.cc index 7c35f9734..aee5a598f 100644 --- a/src/laybasic/laybasic/layWidgets.cc +++ b/src/laybasic/laybasic/layWidgets.cc @@ -600,15 +600,16 @@ LibrarySelectionComboBox::update_list () for (db::LibraryManager::iterator l = db::LibraryManager::instance ().begin (); l != db::LibraryManager::instance ().end (); ++l) { db::Library *lib = db::LibraryManager::instance ().lib (l->second); - if (! m_tech_set || lib->get_technology ().empty () || m_tech == lib->get_technology ()) { + if (! m_tech_set || !lib->for_technologies ()|| lib->is_for_technology (m_tech)) { std::string item_text = lib->get_name (); if (! lib->get_description ().empty ()) { item_text += " - " + lib->get_description (); } - if (m_tech_set && !lib->get_technology ().empty ()) { + if (m_tech_set && lib->for_technologies ()) { item_text += " "; - item_text += tl::to_string (QObject::tr ("[Technology %1]").arg (tl::to_qstring (lib->get_technology ()))); + std::string tn = tl::join (std::vector (lib->get_technologies ().begin (), lib->get_technologies ().end ()), ","); + item_text += tl::to_string (QObject::tr ("[Technology %1]").arg (tl::to_qstring (tn))); } addItem (tl::to_qstring (item_text), QVariant ((unsigned int) lib->get_id ())); diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index 924514fc9..af7cb5105 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -104,6 +104,7 @@ RUBYTEST (dbGlyphs, "dbGlyphs.rb") RUBYTEST (dbInstanceTest, "dbInstanceTest.rb") RUBYTEST (dbInstElementTest, "dbInstElementTest.rb") RUBYTEST (dbLayerMapping, "dbLayerMapping.rb") +RUBYTEST (dbLibrary, "dbLibrary.rb") RUBYTEST (dbLayout, "dbLayout.rb") RUBYTEST (dbLayoutTest, "dbLayoutTest.rb") RUBYTEST (dbLayoutDiff, "dbLayoutDiff.rb") diff --git a/testdata/ruby/dbLibrary.rb b/testdata/ruby/dbLibrary.rb new file mode 100644 index 000000000..cd73007a5 --- /dev/null +++ b/testdata/ruby/dbLibrary.rb @@ -0,0 +1,93 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2020 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class DBLibrary_TestClass < TestBase + + def test_1_registration + + lib = RBA::Library::new + + assert_equal(lib.name, "") + assert_equal(lib.id, 0) + + lib.register("RBA-unit-test") + + assert_equal(lib.name, "RBA-unit-test") + assert_equal(lib.id != 0, true) + + assert_equal(RBA::Library::library_names.member?("RBA-unit-test"), true) + assert_equal(RBA::Library::library_by_name("RBA-unit-test").id, lib.id) + + lib.delete + assert_equal(RBA::Library::library_by_name("RBA-unit-test"), nil) + + end + + def test_2_attributes + + lib = RBA::Library::new + + lib.description = "42 is the answer" + assert_equal(lib.description, "42 is the answer") + + assert_equal(lib.is_for_technology("X"), false) + assert_equal(lib.technologies, []) + assert_equal(lib.for_technologies, false) + + lib.technology = "X" + assert_equal(lib.is_for_technology("X"), true) + assert_equal(lib.technologies, ["X"]) + assert_equal(lib.for_technologies, true) + + lib.technology = "" + assert_equal(lib.is_for_technology("X"), false) + assert_equal(lib.technologies, []) + assert_equal(lib.for_technologies, false) + + lib.add_technology("Y") + assert_equal(lib.is_for_technology("X"), false) + assert_equal(lib.is_for_technology("Y"), true) + assert_equal(lib.technologies, ["Y"]) + assert_equal(lib.for_technologies, true) + + lib.clear_technologies + assert_equal(lib.is_for_technology("Y"), false) + assert_equal(lib.technologies, []) + assert_equal(lib.for_technologies, false) + + end + + def test_3_layout + + lib = RBA::Library::new + lib.layout.create_cell("X") + assert_equal(lib.layout.top_cell.name, "X") + + end + +end + +load("test_epilogue.rb") + From 95111f8aa8f0d568f7fcc4f14b4279d0afe33b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20K=C3=B6fferlein?= Date: Fri, 5 Jun 2020 12:11:02 +0200 Subject: [PATCH 4/4] Implemented #522 (skip // lines in RVE reader) (#583) --- src/rdb/rdb/rdbRVEReader.cc | 50 +++++++++++----- src/rdb/unit_tests/rdb.cc | 2 - src/rdb/unit_tests/rdbRVEReaderTests.cc | 78 +++++++++++++++++++++++++ src/rdb/unit_tests/unit_tests.pro | 1 + 4 files changed, 114 insertions(+), 17 deletions(-) create mode 100644 src/rdb/unit_tests/rdbRVEReaderTests.cc diff --git a/src/rdb/rdb/rdbRVEReader.cc b/src/rdb/rdb/rdbRVEReader.cc index 2bcdff3cc..14e3139af 100644 --- a/src/rdb/rdb/rdbRVEReader.cc +++ b/src/rdb/rdb/rdbRVEReader.cc @@ -83,7 +83,7 @@ public: std::vector points; std::vector edges; - ex = tl::Extractor (m_input_stream.get_line ().c_str ()); + ex = tl::Extractor (get_line ().c_str ()); ex.read (s, " "); ex.read (res); @@ -99,7 +99,7 @@ public: std::string cat_name; id_type waived_tag_id = db.tags ().tag ("waived").id (); - while (! m_input_stream.at_end ()) { + while (! at_end ()) { // TODO: check if this is correct: when a new category is started the // cell name is reset. Any shape not having a specific cell will go into the @@ -108,7 +108,7 @@ public: // Read the category name unless we have some already (that we got when parsing the shapes). if (cat_name.empty ()) { - ex = tl::Extractor (m_input_stream.get_line ().c_str ()); + ex = tl::Extractor (get_line ().c_str ()); const char *start = ex.skip (); if (! *start) { break; @@ -127,11 +127,11 @@ public: Category *cath = db.create_category (cat_name); cat_name.clear (); - if (m_input_stream.at_end ()) { + if (at_end ()) { error (tl::to_string (tr ("Unexpected end of file"))); } - ex = tl::Extractor (m_input_stream.get_line ().c_str ()); + ex = tl::Extractor (get_line ().c_str ()); size_t n1, n2, n3; ex.read (n1); ex.read (n2); @@ -142,11 +142,11 @@ public: std::string desc; for (size_t i = 0; i < n3; ++i) { - if (m_input_stream.at_end ()) { + if (at_end ()) { error (tl::to_string (tr ("Unexpected end of file"))); } - std::string l = m_input_stream.get_line (); + std::string l = get_line (); if (l.size () > 3 && l[0] == 'W' && l[1] == 'E' && isdigit (l[2])) { size_t n = 0; @@ -178,12 +178,12 @@ public: bool waived = (w != waivers.end ()); // TODO: add waiver string somehow ... - if (m_input_stream.at_end ()) { + if (at_end ()) { warn (tl::to_string (tr ("Unexpected end of file before the specified number of shapes was read - stopping."))); break; } - s = m_input_stream.get_line (); + s = get_line (); ex = tl::Extractor (s.c_str ()); @@ -221,11 +221,11 @@ public: while (true) { - if (m_input_stream.at_end ()) { + if (at_end ()) { error (tl::to_string (tr ("Unexpected end of file"))); } - ex = tl::Extractor (m_input_stream.get_line ().c_str ()); + ex = tl::Extractor (get_line ().c_str ()); char c = *ex.skip (); if (isalpha (c)) { @@ -340,11 +340,11 @@ public: if (point > 0) { - if (m_input_stream.at_end ()) { + if (at_end ()) { error (tl::to_string (tr ("Unexpected end of file"))); } - ex = tl::Extractor (m_input_stream.get_line ().c_str ()); + ex = tl::Extractor (get_line ().c_str ()); } @@ -368,11 +368,11 @@ public: if (point > 0) { - if (m_input_stream.at_end ()) { + if (at_end ()) { error (tl::to_string (tr ("Unexpected end of file"))); } - ex = tl::Extractor (m_input_stream.get_line ().c_str ()); + ex = tl::Extractor (get_line ().c_str ()); } @@ -424,6 +424,26 @@ public: private: tl::TextInputStream m_input_stream; tl::AbsoluteProgress m_progress; + std::string m_line; + + bool at_end () + { + return m_input_stream.at_end (); + } + + const std::string &get_line () + { + m_line.clear (); + while (! m_input_stream.at_end ()) { + m_line = m_input_stream.get_line (); + // skip lines starting with "//" (#522) + if (m_line.size () < 2 || m_line[0] != '/' || m_line[1] != '/') { + break; + } + m_line.clear (); + } + return m_line; + } void warn (const std::string &msg) { diff --git a/src/rdb/unit_tests/rdb.cc b/src/rdb/unit_tests/rdb.cc index d4ba26c0a..106f74874 100644 --- a/src/rdb/unit_tests/rdb.cc +++ b/src/rdb/unit_tests/rdb.cc @@ -20,8 +20,6 @@ */ - - #include "rdb.h" #include "tlUnitTest.h" #include "dbBox.h" diff --git a/src/rdb/unit_tests/rdbRVEReaderTests.cc b/src/rdb/unit_tests/rdbRVEReaderTests.cc new file mode 100644 index 000000000..110398787 --- /dev/null +++ b/src/rdb/unit_tests/rdbRVEReaderTests.cc @@ -0,0 +1,78 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "rdb.h" +#include "rdbReader.h" +#include "tlUnitTest.h" +#include "tlFileUtils.h" +#include "tlLog.h" + +void run_rve_test (tl::TestBase *_this, const std::string &fn_rve, const std::string &fn_au) +{ + rdb::Database db; + + { + tl::InputFile input (tl::testsrc_private () + "/testdata/rve/" + fn_rve); + tl::InputStream is (input); + rdb::Reader reader (is); + reader.read (db); + } + + std::string tmp = _this->tmp_file (); + db.save (tmp); + + std::string au_path = tl::absolute_file_path (tl::testsrc_private () + "/testdata/rve/" + fn_au); + + std::string txt, au_txt; + + try { + tl::InputFile input (au_path); + tl::InputStream is (input); + tl::TextInputStream ts (is); + au_txt = ts.read_all (); + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } + + { + tl::InputFile input (tmp); + tl::InputStream is (input); + tl::TextInputStream ts (is); + txt = ts.read_all (); + } + + if (au_txt != txt) { + tl::error << "Golden and actual data differs:"; + tl::error << " cp " << tmp << " " << au_path; + } + EXPECT_EQ (au_txt == txt, true); +} + +TEST(1) +{ + run_rve_test (_this, "rve1.db", "rve1_au.txt"); +} + +TEST(2) +{ + run_rve_test (_this, "rve2.db", "rve2_au.txt"); +} diff --git a/src/rdb/unit_tests/unit_tests.pro b/src/rdb/unit_tests/unit_tests.pro index 2453bc017..ed51891c5 100644 --- a/src/rdb/unit_tests/unit_tests.pro +++ b/src/rdb/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ rdb.cc \ + rdbRVEReaderTests.cc INCLUDEPATH += $$RDB_INC $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$RDB_INC $$TL_INC $$DB_INC $$GSI_INC