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/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/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/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/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/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/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/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/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/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/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 diff --git a/testdata/algo/antenna_au1.gds b/testdata/algo/antenna_au1.gds index 9c56d0a66..7527f097c 100644 Binary files a/testdata/algo/antenna_au1.gds and b/testdata/algo/antenna_au1.gds differ 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 000000000..aacd0f06b Binary files /dev/null and b/testdata/cif/issue_578_au.gds differ 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 2a6e918c2..cb53332ae 100644 Binary files a/testdata/drc/drcSimpleTests_au7.gds and b/testdata/drc/drcSimpleTests_au7.gds differ 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 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") +