diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 6914e12fa..3b5aa2b4e 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -50,6 +50,7 @@ SOURCES = \ dbLibraryManager.cc \ dbLibraryProxy.cc \ dbLoadLayoutOptions.cc \ + dbLog.cc \ dbManager.cc \ dbMatrix.cc \ dbMemStatistics.cc \ @@ -122,6 +123,7 @@ SOURCES = \ gsiDeclDbLayoutUtils.cc \ gsiDeclDbLayoutQuery.cc \ gsiDeclDbLibrary.cc \ + gsiDeclDbLog.cc \ gsiDeclDbManager.cc \ gsiDeclDbMatrix.cc \ gsiDeclDbMetaInfo.cc \ @@ -271,6 +273,7 @@ HEADERS = \ dbLibraryManager.h \ dbLibraryProxy.h \ dbLoadLayoutOptions.h \ + dbLog.h \ dbManager.h \ dbMatrix.h \ dbMemStatistics.h \ diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 2cd32edfd..10022a34e 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -30,6 +30,30 @@ namespace db { +/** + * @brief Creates a joined name for nets and pins + */ +static std::string +join_names (const std::string &n1, const std::string &n2) +{ + // create a new name for the joined net + if (n2.empty ()) { + return n1; + } else if (n1.empty ()) { + return n2; + } else if (n1 == n2) { + return n1; + } else { + // separate parts (if already joined) and mix + auto p1 = tl::split (n1, ","); + auto p2 = tl::split (n2, ","); + std::set ps; + ps.insert (p1.begin (), p1.end ()); + ps.insert (p2.begin (), p2.end ()); + return tl::join (ps.begin (), ps.end (), ","); + } +} + // -------------------------------------------------------------------------------- // Circuit class implementation @@ -366,14 +390,11 @@ void Circuit::remove_net (Net *net) void Circuit::join_nets (Net *net, Net *with) { - if (! net) { - return; - } - if (net == with || ! with) { + if (net == with || ! with || ! net) { return; } if (net->circuit () != this || with->circuit () != this) { - throw tl::Exception (tl::to_string (tr ("Nets not withing given circuit"))); + throw tl::Exception (tl::to_string (tr ("Nets not within given circuit"))); } while (with->begin_terminals () != with->end_terminals ()) { @@ -387,14 +408,16 @@ void Circuit::join_nets (Net *net, Net *with) } while (with->begin_pins () != with->end_pins ()) { - connect_pin (with->begin_pins ()->pin_id (), net); + join_pin_with_net (with->begin_pins ()->pin_id (), net); } - // TODO: join clusters too, join net properties(?) if (netlist ()->callbacks ()) { netlist ()->callbacks ()->link_nets (net, with); } + // create a new name for the joined net + net->set_name (join_names (net->name (), with->name ())); + remove_net (with); } @@ -680,6 +703,69 @@ void Circuit::connect_pin (size_t pin_id, Net *net) } } +void Circuit::join_pin_with_net (size_t pin_id, Net *net) +{ + if (net_for_pin (pin_id) == net) { + return; + } + + if (pin_id < m_pin_refs.size ()) { + Net::pin_iterator p = m_pin_refs [pin_id]; + if (! tl::is_null_iterator (p) && p->net ()) { + p->net ()->erase_pin (p); + } + m_pin_refs [pin_id] = Net::pin_iterator (); + } + + if (net) { + if (net->begin_pins () != net->end_pins ()) { + join_pins (net->begin_pins ()->pin_id (), pin_id); + } else { + net->add_pin (NetPinRef (pin_id)); + } + } +} + +void Circuit::join_pins (size_t pin, size_t with) +{ + if (with != pin && with < m_pin_by_id.size () && ! tl::is_null_iterator (m_pin_by_id [with])) { + + // create a new joined name + m_pin_by_id [pin]->set_name (join_names (m_pin_by_id [pin]->name (), m_pin_by_id [with]->name ())); + + m_pins.erase (m_pin_by_id [with]); + m_pin_by_id.erase (m_pin_by_id.begin () + with); + m_pin_refs.erase (m_pin_refs.begin () + with); + + // correct the pin IDs inside the circuit: all IDS > with will be reduced by 1 + if (pin > with) { + --pin; + } + for (auto p = m_pins.begin (); p != m_pins.end (); ++p) { + if (p->id () > with) { + p->set_id (p->id () - 1); + } + } + for (auto p = m_pin_refs.begin () + with; p != m_pin_refs.end (); ++p) { + (*p)->set_pin_id ((*p)->pin_id () - 1); + } + + // join nets in calls + for (auto s = begin_refs (); s != end_refs (); ++s) { + + db::SubCircuit &sc = *s; + db::Net *with_net = sc.net_for_pin (with); + + // NOTE: this will also correct the Pin IDs on the attached nets + sc.erase_pin (with); + + sc.circuit ()->join_nets (sc.net_for_pin (pin), with_net); + + } + + } +} + void Circuit::purge_nets_keep_pins () { do_purge_nets (true); diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 4ffad288a..22253a78c 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -702,6 +702,15 @@ public: */ void connect_pin (size_t pin_id, Net *net); + /** + * @brief Adds a pin to the given net + * The pin will be added to the net. If there is already a pin + * on the net, the existing and new pin will be joined. + * This usually implies that nets further up in the hierarchy + * are joined too. + */ + void join_pin_with_net (size_t pin_id, Net *net); + /** * @brief Renames the pin with the given ID */ @@ -819,7 +828,7 @@ private: bool combine_parallel_devices (const db::DeviceClass &cls); bool combine_serial_devices (const db::DeviceClass &cls); void do_purge_nets (bool keep_pins); - + void join_pins (size_t pin_id, size_t with); void devices_changed (); void subcircuits_changed (); void nets_changed (); diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 83395e612..ae2972a1b 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -33,6 +33,7 @@ #include "dbLayoutToNetlistFormatDefs.h" #include "dbLayoutVsSchematicFormatDefs.h" #include "dbShapeProcessor.h" +#include "dbLog.h" #include "tlGlobPattern.h" namespace db @@ -44,7 +45,7 @@ namespace db // Note: the iterator provides the hierarchical selection (enabling/disabling cells etc.) LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) - : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false) + : m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) { // check the iterator if (iter.has_complex_region () || iter.region () != db::Box::world ()) { @@ -64,7 +65,7 @@ LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter) } LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index) - : mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false) + : mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) { if (dss->is_valid_layout_index (m_layout_index)) { m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set ()); @@ -72,7 +73,7 @@ LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_i } LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) - : m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0), m_include_floating_subcircuits (false) + : m_iter (), m_netlist_extracted (false), m_is_flat (true), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) { mp_internal_dss.reset (new db::DeepShapeStore (topcell_name, dbu)); mp_dss.reset (mp_internal_dss.get ()); @@ -83,7 +84,7 @@ LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu) LayoutToNetlist::LayoutToNetlist () : m_iter (), mp_internal_dss (new db::DeepShapeStore ()), mp_dss (mp_internal_dss.get ()), m_layout_index (0), - m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false) + m_netlist_extracted (false), m_is_flat (false), m_device_scaling (1.0), m_include_floating_subcircuits (false), m_top_level_mode (false) { init (); } @@ -238,8 +239,14 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co if (m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); } + ensure_netlist (); + + extractor.clear_log_entries (); extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters, m_device_scaling); + + // transfer errors to log entries + m_log_entries.insert (m_log_entries.end (), extractor.begin_log_entries (), extractor.end_log_entries ()); } void LayoutToNetlist::reset_extracted () @@ -249,6 +256,8 @@ void LayoutToNetlist::reset_extracted () m_net_clusters.clear (); mp_netlist.reset (0); + m_log_entries.clear (); + m_netlist_extracted = false; } @@ -358,49 +367,12 @@ void LayoutToNetlist::extract_netlist () } ensure_netlist (); - const db::Layout &layout = dss ().layout (m_layout_index); - db::NetlistExtractor netex; - - netex.set_joined_net_names (m_joined_net_names); - - std::map > jp_per_cell; - for (std::list >::const_iterator j = m_joined_net_names_per_cell.begin (); j != m_joined_net_names_per_cell.end (); ++j) { - if (j->first.is_const ()) { - jp_per_cell [j->first.pattern ()].push_back (j->second); - } else { - for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { - if (j->first.match (layout.cell_name (c->cell_index ()))) { - jp_per_cell [layout.cell_name (c->cell_index ())].push_back (j->second); - } - } - } - } - for (std::map >::const_iterator i = jp_per_cell.begin (); i != jp_per_cell.end (); ++i) { - netex.set_joined_net_names (i->first, i->second); - } - - netex.set_joined_nets (m_joined_nets); - - std::map > > jn_per_cell; - for (std::list > >::const_iterator j = m_joined_nets_per_cell.begin (); j != m_joined_nets_per_cell.end (); ++j) { - if (j->first.is_const ()) { - jn_per_cell [j->first.pattern ()].push_back (j->second); - } else { - for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { - if (j->first.match (layout.cell_name (c->cell_index ()))) { - jn_per_cell [layout.cell_name (c->cell_index ())].push_back (j->second); - } - } - } - } - for (std::map > >::const_iterator i = jn_per_cell.begin (); i != jn_per_cell.end (); ++i) { - netex.set_joined_nets (i->first, i->second); - } - netex.set_include_floating_subcircuits (m_include_floating_subcircuits); netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters); + do_join_nets (); + if (tl::verbosity () >= 41) { MemStatisticsCollector m (false); mem_stat (&m, db::MemStatistics::None, 0); @@ -410,6 +382,197 @@ void LayoutToNetlist::extract_netlist () m_netlist_extracted = true; } +void LayoutToNetlist::check_extraction_errors () +{ + int num_errors = 0; + int max_errors = 10; + std::string errors; + for (auto l = m_log_entries.begin (); l != m_log_entries.end (); ++l) { + if (l->severity () >= db::Error) { + errors += "\n"; + if (++num_errors >= max_errors) { + errors += "...\n"; + errors += tl::sprintf (tl::to_string (tr ("(list shortened after %d errrors, see log for all errors)")), max_errors); + break; + } else { + errors += l->to_string (); + } + } + } + + if (num_errors > 0) { + throw tl::Exception (tl::to_string (tr ("Errors encountered during netlist extraction:")) + errors); + } +} + +void LayoutToNetlist::join_nets_from_pattern (db::Circuit &c, const tl::GlobPattern &p) +{ + std::map > nets_by_name; + for (auto n = c.begin_nets (); n != c.end_nets (); ++n) { + if (! n->name ().empty () && p.match (n->name ())) { + nets_by_name [n->name ()].push_back (n.operator-> ()); + } + } + + for (auto n2n = nets_by_name.begin (); n2n != nets_by_name.end (); ++n2n) { + if (n2n->second.size () > 1) { + do_join_nets (c, n2n->second); + } + } +} + +void LayoutToNetlist::join_nets_from_pattern (db::Circuit &c, const std::set &p) +{ + // NOTE: this version implies implicit joining of different nets with the same name from the set p + std::vector nets; + for (auto n = c.begin_nets (); n != c.end_nets (); ++n) { + if (! n->name ().empty () && p.find (n->name ()) != p.end ()) { + nets.push_back (n.operator-> ()); + } + } + + if (nets.size () > 1) { + do_join_nets (c, nets); + } +} + +void LayoutToNetlist::do_join_nets (db::Circuit &c, const std::vector &nets) +{ + if (nets.size () <= 1) { + return; + } + + for (auto n = nets.begin () + 1; n != nets.end (); ++n) { + check_must_connect (c, *nets [0], **n); + c.join_nets (nets [0], *n); + } +} + +static std::string subcircuit_to_string (const db::SubCircuit &sc) +{ + if (! sc.name ().empty ()) { + return tl::to_string (tr (" on subcircuit ")) + sc.name (); + } else { + return std::string (); + } +} + +static db::DPolygon subcircuit_geometry (const db::SubCircuit &sc, const db::Layout *layout) +{ + if (! layout || ! sc.circuit_ref () || ! layout->is_valid_cell_index (sc.circuit_ref ()->cell_index ())) { + return db::DPolygon (); + } + + db::DBox dbox = db::CplxTrans (layout->dbu ()) * layout->cell (sc.circuit_ref ()->cell_index ()).bbox (); + return db::DPolygon (sc.trans () * dbox); +} + +void LayoutToNetlist::check_must_connect (const db::Circuit &c, const db::Net &a, const db::Net &b) +{ + if (&a == &b) { + return; + } + + if (c.begin_refs () != c.end_refs ()) { + if (a.begin_pins () == a.end_pins ()) { + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a.expanded_name ())); + error.set_cell_name (c.name ()); + error.set_category_name ("must-connect"); + log_entry (error); + } + if (b.begin_pins () == b.end_pins ()) { + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s is not connected to outside")), a.expanded_name ())); + error.set_cell_name (c.name ()); + error.set_category_name ("must-connect"); + log_entry (error); + } + } else { + if (a.expanded_name () == b.expanded_name ()) { + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s must be connected further up in the hierarchy - this is an error at chip top level")), a.expanded_name ())); + warn.set_cell_name (c.name ()); + warn.set_category_name ("must-connect"); + log_entry (warn); + } else { + db::LogEntryData warn (m_top_level_mode ? db::Error : db::Warning, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s must be connected further up in the hierarchy - this is an error at chip top level")), a.expanded_name (), b.expanded_name ())); + warn.set_cell_name (c.name ()); + warn.set_category_name ("must-connect"); + log_entry (warn); + } + } + + if (a.begin_pins () != a.end_pins () && b.begin_pins () != b.end_pins ()) { + for (auto ref = c.begin_refs (); ref != c.end_refs (); ++ref) { + const db::SubCircuit &sc = *ref; + // TODO: consider the case of multiple pins on a net (rare) + const db::Net *net_a = sc.net_for_pin (a.begin_pins ()->pin_id ()); + const db::Net *net_b = sc.net_for_pin (b.begin_pins ()->pin_id ()); + if (net_a == 0) { + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), a.expanded_name (), c.name (), subcircuit_to_string (sc))); + error.set_cell_name (sc.circuit ()->name ()); + error.set_geometry (subcircuit_geometry (sc, internal_layout ())); + error.set_category_name ("must-connect"); + log_entry (error); + } + if (net_b == 0) { + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect net %s of circuit %s is not connected at all%s")), b.expanded_name (), c.name (), subcircuit_to_string (sc))); + error.set_cell_name (sc.circuit ()->name ()); + error.set_geometry (subcircuit_geometry (sc, internal_layout ())); + error.set_category_name ("must-connect"); + log_entry (error); + } + if (net_a && net_b && net_a != net_b) { + if (a.expanded_name () == b.expanded_name ()) { + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect nets %s of circuit %s are not connected%s")), a.expanded_name (), c.name (), subcircuit_to_string (sc))); + error.set_cell_name (sc.circuit ()->name ()); + error.set_geometry (subcircuit_geometry (sc, internal_layout ())); + error.set_category_name ("must-connect"); + log_entry (error); + } else { + db::LogEntryData error (db::Error, tl::sprintf (tl::to_string (tr ("Must-connect nets %s and %s of circuit %s are not connected%s")), a.expanded_name (), b.expanded_name (), c.name (), subcircuit_to_string (sc))); + error.set_cell_name (sc.circuit ()->name ()); + error.set_geometry (subcircuit_geometry (sc, internal_layout ())); + error.set_category_name ("must-connect"); + log_entry (error); + } + } + } + } +} + +void LayoutToNetlist::do_join_nets () +{ + if (! mp_netlist) { + return; + } + + // prevents updates + NetlistLocker locked_netlist (mp_netlist.get ()); + + for (auto c = mp_netlist->begin_top_down (); c != mp_netlist->end_top_down (); ++c) { + + for (auto jn = m_joined_net_names.begin (); jn != m_joined_net_names.end (); ++jn) { + join_nets_from_pattern (*c, *jn); + } + + for (auto jn = m_joined_nets.begin (); jn != m_joined_nets.end (); ++jn) { + join_nets_from_pattern (*c, *jn); + } + + for (auto jn = m_joined_net_names_per_cell.begin (); jn != m_joined_net_names_per_cell.end (); ++jn) { + if (jn->first.match (c->name ())) { + join_nets_from_pattern (*c, jn->second); + } + } + + for (auto jn = m_joined_nets_per_cell.begin (); jn != m_joined_nets_per_cell.end (); ++jn) { + if (jn->first.match (c->name ())) { + join_nets_from_pattern (*c, jn->second); + } + } + + } +} + void LayoutToNetlist::mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { if (! no_self) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index e6bb022af..bfa07f98e 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -28,6 +28,7 @@ #include "dbNetlistExtractor.h" #include "dbNetlistDeviceExtractor.h" #include "dbLayoutToNetlistEnums.h" +#include "dbLog.h" #include "tlGlobPattern.h" namespace db @@ -78,6 +79,7 @@ class DB_PUBLIC LayoutToNetlist { public: typedef std::map::const_iterator layer_iterator; + typedef std::vector log_entries_type; /** * @brief The constructor @@ -190,6 +192,69 @@ public: m_filename = filename; } + /** + * @brief Gets the top level mode flag + */ + bool top_level_mode () const + { + return m_top_level_mode; + } + + /** + * @brief Sets top level mode + * + * In top level mode, must-connect warnings are turned into + * errors for example. + * + * By default, top-level mode is off. + */ + void set_top_level_mode (bool f) + { + m_top_level_mode = f; + } + + /** + * @brief Gets the log entries + */ + const log_entries_type &log_entries () const + { + return m_log_entries; + } + + /** + * @brief Iterator for the log entries (begin) + */ + log_entries_type::const_iterator begin_log_entries () const + { + return m_log_entries.begin (); + } + + /** + * @brief Iterator for the log entries (end) + */ + log_entries_type::const_iterator end_log_entries () const + { + return m_log_entries.end (); + } + + /** + * @brief Clears the log entries + */ + void clear_log_entries () + { + m_log_entries.clear (); + } + + /** + * @brief Adds a log entry + */ + void log_entry (const db::LogEntryData &log_entry) + { + if (m_log_entries.empty () || m_log_entries.back () != log_entry) { + m_log_entries.push_back (log_entry); + } + } + /** * @brief Sets the number of threads to use for operations which support multiple threads */ @@ -380,7 +445,8 @@ public: * boolean operations for deriving layers. Other operations are applicable as long as they are * capable of delivering hierarchical layers. * - * If errors occur, the device extractor will contain theses errors. + * If errors occur, the device extractor will contain theses errors. They are also transferred + * to the LayoutToNetlist object. */ void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map &layers); @@ -388,7 +454,7 @@ public: * @brief Resets the extracted netlist * * This method will invalidate the netlist and extraction. It is called automatically when - * cone of the connect methods is called. + * one of the connect methods is called. */ void reset_extracted (); @@ -558,6 +624,11 @@ public: */ void extract_netlist (); + /** + * @brief Throws an exception if the extractor contains errors + */ + void check_extraction_errors (); + /** * @brief Marks the netlist as extracted * NOTE: this method is provided for special cases such as netlist readers. Don't @@ -930,6 +1001,7 @@ private: std::string m_name; std::string m_original_file; std::string m_filename; + log_entries_type m_log_entries; db::RecursiveShapeIterator m_iter; std::unique_ptr mp_internal_dss; tl::weak_ptr mp_dss; @@ -948,6 +1020,7 @@ private: db::DeepLayer m_dummy_layer; std::string m_generator; bool m_include_floating_subcircuits; + bool m_top_level_mode; std::list m_joined_net_names; std::list > m_joined_net_names_per_cell; std::list > m_joined_nets; @@ -963,6 +1036,11 @@ private: void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b); size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn); bool is_persisted_impl (const db::ShapeCollection &coll) const; + void do_join_nets (db::Circuit &c, const std::vector &nets); + void do_join_nets (); + void join_nets_from_pattern (db::Circuit &c, const tl::GlobPattern &p); + void join_nets_from_pattern (db::Circuit &c, const std::set &p); + void check_must_connect (const db::Circuit &c, const db::Net &a, const db::Net &b); // implementation of NetlistManipulationCallbacks virtual size_t link_net_to_parent_circuit (const Net *subcircuit_net, Circuit *parent_circuit, const DCplxTrans &trans); diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc index 133a4cbd3..3c54a06d8 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.cc +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -56,8 +56,15 @@ namespace l2n_std_format DB_PUBLIC std::string LongKeys::mirror_key ("mirror"); DB_PUBLIC std::string LongKeys::scale_key ("scale"); DB_PUBLIC std::string LongKeys::pin_key ("pin"); + DB_PUBLIC std::string LongKeys::message_key ("message"); - // A, B, C, D, E, F, G, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y + DB_PUBLIC std::string LongKeys::info_severity_key ("info"); + DB_PUBLIC std::string LongKeys::warning_severity_key ("warning"); + DB_PUBLIC std::string LongKeys::error_severity_key ("error"); + DB_PUBLIC std::string LongKeys::cell_key ("cell"); + DB_PUBLIC std::string LongKeys::cat_key ("cat"); + + // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y DB_PUBLIC std::string ShortKeys::version_key ("V"); DB_PUBLIC std::string ShortKeys::description_key ("B"); DB_PUBLIC std::string ShortKeys::top_key ("W"); @@ -82,6 +89,15 @@ namespace l2n_std_format DB_PUBLIC std::string ShortKeys::mirror_key ("M"); DB_PUBLIC std::string ShortKeys::scale_key ("S"); DB_PUBLIC std::string ShortKeys::pin_key ("P"); + DB_PUBLIC std::string ShortKeys::message_key ("H"); + + // I, W, E, C, X + + DB_PUBLIC std::string ShortKeys::info_severity_key ("I"); + DB_PUBLIC std::string ShortKeys::warning_severity_key ("W"); + DB_PUBLIC std::string ShortKeys::error_severity_key ("E"); + DB_PUBLIC std::string ShortKeys::cell_key ("C"); + DB_PUBLIC std::string ShortKeys::cat_key ("X"); } } diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index b0818ff47..8f5ec1d7c 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -47,7 +47,7 @@ namespace db * (circuits before subcircuits, nets before use ...) * * Main body: - * [version|description|unit|top|layer|connect|global|circuit|class|device|any]* + * [version|description|unit|top|layer|connect|global|circuit|class|device|message-entry|any]* * * [version]: * version() - file format version [short key: V] @@ -144,7 +144,7 @@ namespace db * coordinates are bottom/left and top/right * * [text]: - * text( [text] [coord]) - defines a rectangle [short key: J] + * text( [coord]) - defines a label [short key: J] * * [coord]: * - absolute coordinates @@ -181,6 +181,26 @@ namespace db * mirror - if specified, the instance is mirrored before rotation [short key: M] * scale() - magnification (default is 1) [short key: S] * + * [message-entry]: + * message([severity] [message|message-geometry|message-cell|message-category|any]*) - message entry [short key: H] + * + * [message]: + * description() - message text [short key: B] + * + * [message-geometry]: + * polygon() - message geometry polygon in string-serialized form [short key: Q] + * + * [message-cell]: + * cell() - message cell [short key: C] + * + * [message-category]: + * cat( ?) - message category with optional description [short key: X] + * + * [severity]: + * info | - [short key: I] + * warning | - [short key: W] + * error - [short key: E] + * * [any]: * * | * | @@ -220,8 +240,14 @@ namespace l2n_std_format static std::string mirror_key; static std::string scale_key; static std::string pin_key; + static std::string message_key; static std::string indent1; static std::string indent2; + static std::string info_severity_key; + static std::string warning_severity_key; + static std::string error_severity_key; + static std::string cell_key; + static std::string cat_key; }; struct DB_PUBLIC LongKeys @@ -253,8 +279,14 @@ namespace l2n_std_format static std::string mirror_key; static std::string scale_key; static std::string pin_key; + static std::string message_key; static std::string indent1; static std::string indent2; + static std::string info_severity_key; + static std::string warning_severity_key; + static std::string error_severity_key; + static std::string cell_key; + static std::string cat_key; }; template struct DB_PUBLIC keys; diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index 033a8fb79..365e9d828 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -132,6 +132,7 @@ LayoutToNetlistStandardReader::skip () { while (m_ex.at_end () || *m_ex.skip () == '#') { if (m_stream.at_end ()) { + m_ex = tl::Extractor (); return; } m_progress.set (m_stream.line_number ()); @@ -184,6 +185,108 @@ void LayoutToNetlistStandardReader::skip_element () } } +bool LayoutToNetlistStandardReader::read_message (std::string &msg) +{ + if (test (skeys::description_key) || test (lkeys::description_key)) { + Brace br (this); + read_word_or_quoted (msg); + br.done (); + return true; + } else { + return false; + } +} + +bool LayoutToNetlistStandardReader::read_severity (db::Severity &severity) +{ + if (test (skeys::info_severity_key) || test (lkeys::info_severity_key)) { + severity = db::Info; + return true; + } else if (test (skeys::warning_severity_key) || test (lkeys::warning_severity_key)) { + severity = db::Warning; + return true; + } else if (test (skeys::error_severity_key) || test (lkeys::error_severity_key)) { + severity = db::Error; + return true; + } else { + return false; + } +} + +bool LayoutToNetlistStandardReader::read_message_cell (std::string &cell_name) +{ + if (test (skeys::cell_key) || test (lkeys::cell_key)) { + Brace br (this); + read_word_or_quoted (cell_name); + br.done (); + return true; + } else { + return false; + } +} + +bool LayoutToNetlistStandardReader::read_message_geometry (db::DPolygon &polygon) +{ + if (test (skeys::polygon_key) || test (lkeys::polygon_key)) { + Brace br (this); + std::string s; + read_word_or_quoted (s); + tl::Extractor ex (s.c_str ()); + ex.read (polygon); + br.done (); + return true; + } else { + return false; + } +} + +bool LayoutToNetlistStandardReader::read_message_cat (std::string &category_name, std::string &category_description) +{ + if (test (skeys::cat_key) || test (lkeys::cat_key)) { + Brace br (this); + read_word_or_quoted (category_name); + if (br) { + read_word_or_quoted (category_description); + } + br.done (); + return true; + } else { + return false; + } +} + +void LayoutToNetlistStandardReader::read_message_entry (db::LogEntryData &data) +{ + Severity severity (db::NoSeverity); + std::string msg, cell_name, category_name, category_description; + db::DPolygon geometry; + + Brace br (this); + while (br) { + if (read_severity (severity)) { + // continue + } else if (read_message (msg)) { + // continue + } else if (read_message_cell (cell_name)) { + // continue + } else if (read_message_cat (category_name, category_description)) { + // continue + } else if (read_message_geometry (geometry)) { + // continue + } else { + skip_element (); + } + } + br.done (); + + data.set_severity (severity); + data.set_message (msg); + data.set_cell_name (cell_name); + data.set_category_description (category_description); + data.set_category_name (category_name); + data.set_geometry (geometry); +} + void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("File read: ")) + m_path); @@ -359,6 +462,13 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo } br.done (); + } else if (l2n && (test (skeys::message_key) || test (lkeys::message_key))) { + + db::LogEntryData data; + read_message_entry (data); + + l2n->log_entry (data); + } else if (l2n && (test (skeys::global_key) || test (lkeys::global_key))) { Brace br (this); diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index 46c917d3f..fcb8eb245 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -109,6 +109,8 @@ protected: void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, Brace *nested = 0, std::map *map_per_circuit = 0); static size_t terminal_id (const db::DeviceClass *device_class, const std::string &tname); static std::pair device_model_by_name (db::Netlist *netlist, const std::string &dmname); + bool read_message (std::string &msg); + bool read_severity (Severity &severity); const std::string &path () const { @@ -140,6 +142,15 @@ protected: void skip (); void skip_element (); +private: + tl::TextInputStream m_stream; + std::string m_path; + std::string m_line; + double m_dbu; + tl::Extractor m_ex; + db::Point m_ref; + tl::AbsoluteProgress m_progress; + void read_net (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map); void read_pin (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map); void read_device (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map, std::map > &connections); @@ -151,15 +162,10 @@ protected: db::Box read_rect (); void read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster &lc, db::Cell &cell); db::Point read_point (); - -private: - tl::TextInputStream m_stream; - std::string m_path; - std::string m_line; - double m_dbu; - tl::Extractor m_ex; - db::Point m_ref; - tl::AbsoluteProgress m_progress; + void read_message_entry (db::LogEntryData &data); + bool read_message_cell (std::string &cell_name); + bool read_message_geometry (db::DPolygon &polygon); + bool read_message_cat (std::string &category_name, std::string &category_description); }; } diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index 1713ee504..b188d7876 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -130,6 +130,41 @@ TokenizedOutput &TokenizedOutput::operator<< (const std::string &s) // ------------------------------------------------------------------------------------------- +static void write_point (TokenizedOutput &out, const db::Point &pt, db::Point &ref, bool relative) +{ + if (relative) { + + TokenizedOutput (out, std::string (), true) << tl::to_string (pt.x () - ref.x ()) << tl::to_string (pt.y () - ref.y ()); + + } else { + + if (pt.x () == 0 || pt.x () != ref.x ()) { + out << tl::to_string (pt.x ()); + } else { + out << "*"; + } + + if (pt.y () == 0 || pt.y () != ref.y ()) { + out << tl::to_string (pt.y ()); + } else { + out << "*"; + } + + } + + ref = pt; +} + +template +static void write_points (TokenizedOutput &out, const T &poly, const Tr &tr, db::Point &ref, bool relative) +{ + for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) { + write_point (out, tr * *c, ref, relative); + } +} + +// ------------------------------------------------------------------------------------------- + namespace l2n_std_format { @@ -142,6 +177,54 @@ std_writer_impl::std_writer_impl (tl::OutputStream &stream, double dbu, co m_progress.set_unit (1024 * 1024); } +template +std::string std_writer_impl::message_to_s (const std::string &msg) +{ + if (msg.empty ()) { + return std::string (); + } else { + return Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")"; + } +} + +template +std::string std_writer_impl::severity_to_s (const db::Severity severity) +{ + if (severity == db::Info) { + return Keys::info_severity_key; + } else if (severity == db::Warning) { + return Keys::warning_severity_key; + } else if (severity == db::Error) { + return Keys::error_severity_key; + } else { + return std::string (); + } +} + +template +void std_writer_impl::write_log_entry (TokenizedOutput &stream, const LogEntryData &le) +{ + stream << severity_to_s (le.severity ()); + stream << message_to_s (le.message ()); + + if (! le.cell_name ().empty ()) { + TokenizedOutput (stream, Keys::cell_key, true) << tl::to_word_or_quoted_string (le.cell_name ()); + } + + if (! le.category_name ().empty ()) { + TokenizedOutput o (stream, Keys::cat_key, true); + o << tl::to_word_or_quoted_string (le.category_name ()); + if (! le.category_description ().empty ()) { + o << tl::to_word_or_quoted_string (le.category_description ()); + } + } + + if (le.geometry () != db::DPolygon ()) { + TokenizedOutput o (stream, Keys::polygon_key, true); + o << tl::to_word_or_quoted_string (le.geometry ().to_string ()); + } +} + static std::string name_for_layer (const db::LayoutToNetlist *l2n, unsigned int l) { std::string n = l2n->name (l); @@ -318,6 +401,17 @@ void std_writer_impl::write (bool nested, TokenizedOutput &stream, std::ma } + if (! mp_l2n->log_entries ().empty ()) { + if (! Keys::is_short ()) { + stream << endl << "# Log entries" << endl; + } + for (auto l = mp_l2n->begin_log_entries (); l != mp_l2n->end_log_entries (); ++l) { + TokenizedOutput out (stream, Keys::message_key); + this->write_log_entry (out, *l); + m_progress.set (mp_stream->pos ()); + } + } + } if (mp_netlist->begin_device_classes () != mp_netlist->end_device_classes () && ! Keys::is_short ()) { @@ -361,39 +455,6 @@ void std_writer_impl::write (bool nested, TokenizedOutput &stream, std::ma } } -static void write_point (TokenizedOutput &out, const db::Point &pt, db::Point &ref, bool relative) -{ - if (relative) { - - TokenizedOutput (out, std::string (), true) << tl::to_string (pt.x () - ref.x ()) << tl::to_string (pt.y () - ref.y ()); - - } else { - - if (pt.x () == 0 || pt.x () != ref.x ()) { - out << tl::to_string (pt.x ()); - } else { - out << "*"; - } - - if (pt.y () == 0 || pt.y () != ref.y ()) { - out << tl::to_string (pt.y ()); - } else { - out << "*"; - } - - } - - ref = pt; -} - -template -static void write_points (TokenizedOutput &out, const T &poly, const Tr &tr, db::Point &ref, bool relative) -{ - for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) { - write_point (out, tr * *c, ref, relative); - } -} - template void std_writer_impl::write (TokenizedOutput &stream, const db::Circuit &circuit, std::map > *net2id_per_circuit) { diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h index 8032e2651..26434e06b 100644 --- a/src/db/db/dbLayoutToNetlistWriter.h +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -28,6 +28,7 @@ #include "dbTrans.h" #include "dbPolygon.h" #include "dbHierNetworkProcessor.h" +#include "dbLog.h" #include "tlStream.h" #include "tlProgress.h" @@ -43,6 +44,7 @@ class Net; class Netlist; class LayoutToNetlist; class NetShape; +class LogEntryData; /** * @brief A helper class to produce token/list lines @@ -92,6 +94,10 @@ protected: return *mp_stream; } + std::string severity_to_s (const db::Severity severity); + std::string message_to_s (const std::string &msg); + void write_log_entry (TokenizedOutput &stream, const LogEntryData &log_entry); + private: tl::OutputStream *mp_stream; db::Point m_ref; diff --git a/src/db/db/dbLayoutVsSchematicFormatDefs.cc b/src/db/db/dbLayoutVsSchematicFormatDefs.cc index 62de797d0..12f88601e 100644 --- a/src/db/db/dbLayoutVsSchematicFormatDefs.cc +++ b/src/db/db/dbLayoutVsSchematicFormatDefs.cc @@ -43,11 +43,7 @@ namespace lvs_std_format DB_PUBLIC std::string LongKeys::warning_key ("warning"); DB_PUBLIC std::string LongKeys::skipped_key ("skipped"); - DB_PUBLIC std::string LongKeys::info_severity_key ("info"); - DB_PUBLIC std::string LongKeys::warning_severity_key ("warning"); - DB_PUBLIC std::string LongKeys::error_severity_key ("error"); - - // E, H, I, J, L, M, S, W, X, Z, 0, 1 + // H, J, L, M, S, X, Z, 0, 1 DB_PUBLIC std::string ShortKeys::reference_key ("H"); DB_PUBLIC std::string ShortKeys::layout_key ("J"); @@ -60,10 +56,6 @@ namespace lvs_std_format DB_PUBLIC std::string ShortKeys::nomatch_key ("X"); DB_PUBLIC std::string ShortKeys::warning_key ("W"); DB_PUBLIC std::string ShortKeys::skipped_key ("S"); - - DB_PUBLIC std::string ShortKeys::info_severity_key ("I"); - DB_PUBLIC std::string ShortKeys::warning_severity_key ("W"); - DB_PUBLIC std::string ShortKeys::error_severity_key ("E"); } } diff --git a/src/db/db/dbLayoutVsSchematicFormatDefs.h b/src/db/db/dbLayoutVsSchematicFormatDefs.h index 9703a70b8..80abceaac 100644 --- a/src/db/db/dbLayoutVsSchematicFormatDefs.h +++ b/src/db/db/dbLayoutVsSchematicFormatDefs.h @@ -67,7 +67,7 @@ namespace db * Content is the Netlist dump (reduced version of LayoutToNetlist) * * [xrefs]: - * xref([xref|any]*) - cross-reference part [short key: Z] + * xref([xref|log|any]*) - cross-reference part [short key: Z] * * [xref]: * circuit([non] [non] [status|message|log|circuit-xrefs|any]*) @@ -82,11 +82,6 @@ namespace db * [log-entry]: * entry([severity] [message|any]*) - log entry [short key: M] * - * [severity]: - * info | - [short key: I] - * warning | - [short key: W] - * error - [short key: E] - * * [circuit-xrefs]: * xref([xref-pin|xref-device|xref-circuit|xref-net|any]*) * - circuit cross-reference part [short key: Z] @@ -113,6 +108,11 @@ namespace db * [message]: * description() - error description [short key: B] * + * [severity]: + * info | - [short key: I] + * warning | - [short key: W] + * error - [short key: E] + * * [status]: * mismatch | - [short key: 0] * match | - [short key: 1] @@ -145,10 +145,6 @@ namespace lvs_std_format static std::string nomatch_key; static std::string warning_key; static std::string skipped_key; - - static std::string info_severity_key; - static std::string warning_severity_key; - static std::string error_severity_key; }; struct DB_PUBLIC LongKeys @@ -166,10 +162,6 @@ namespace lvs_std_format static std::string nomatch_key; static std::string warning_key; static std::string skipped_key; - - static std::string info_severity_key; - static std::string warning_severity_key; - static std::string error_severity_key; }; template struct DB_PUBLIC keys; diff --git a/src/db/db/dbLayoutVsSchematicReader.cc b/src/db/db/dbLayoutVsSchematicReader.cc index 42f76e27f..f486a19eb 100644 --- a/src/db/db/dbLayoutVsSchematicReader.cc +++ b/src/db/db/dbLayoutVsSchematicReader.cc @@ -116,18 +116,6 @@ void LayoutVsSchematicStandardReader::read_netlist (db::LayoutVsSchematic *lvs) } } -bool LayoutVsSchematicStandardReader::read_message (std::string &msg) -{ - if (test (skeys::description_key) || test (lkeys::description_key)) { - Brace br (this); - read_word_or_quoted (msg); - br.done (); - return true; - } else { - return false; - } -} - bool LayoutVsSchematicStandardReader::read_status (db::NetlistCrossReference::Status &status) { if (test (skeys::match_key) || test (lkeys::match_key)) { @@ -150,25 +138,9 @@ bool LayoutVsSchematicStandardReader::read_status (db::NetlistCrossReference::St } } -bool LayoutVsSchematicStandardReader::read_severity (db::NetlistCrossReference::Severity &severity) -{ - if (test (skeys::info_severity_key) || test (lkeys::info_severity_key)) { - severity = db::NetlistCrossReference::Info; - return true; - } else if (test (skeys::warning_severity_key) || test (lkeys::warning_severity_key)) { - severity = db::NetlistCrossReference::Warning; - return true; - } else if (test (skeys::error_severity_key) || test (lkeys::error_severity_key)) { - severity = db::NetlistCrossReference::Error; - return true; - } else { - return false; - } -} - void LayoutVsSchematicStandardReader::read_log_entry (db::NetlistCrossReference *xref) { - db::NetlistCrossReference::Severity severity = db::NetlistCrossReference::NoSeverity; + db::Severity severity = db::NoSeverity; std::string msg; Brace br (this); @@ -183,10 +155,12 @@ void LayoutVsSchematicStandardReader::read_log_entry (db::NetlistCrossReference } br.done (); + // NOTE: this API does not use the full feature set of db::LogEntryData, so + // we do not use this object here. xref->log_entry (severity, msg); } -void LayoutVsSchematicStandardReader::read_logs_for_circuits (db::NetlistCrossReference *xref) +void LayoutVsSchematicStandardReader::read_logs (db::NetlistCrossReference *xref) { Brace br (this); while (br) { @@ -194,7 +168,7 @@ void LayoutVsSchematicStandardReader::read_logs_for_circuits (db::NetlistCrossRe if (test (skeys::log_entry_key) || test (lkeys::log_entry_key)) { read_log_entry (xref); } else if (at_end ()) { - throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (net, pin, device or circuit expected)"))); + throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside log section (entry expected)"))); } else { skip_element (); } @@ -269,7 +243,7 @@ void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref } else if (test (skeys::xref_key) || test (lkeys::xref_key)) { read_xrefs_for_circuits (xref, circuit_a, circuit_b); } else if (test (skeys::log_key) || test (lkeys::log_key)) { - read_logs_for_circuits (xref); + read_logs (xref); } else if (at_end ()) { throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (status keyword of xrefs expected)"))); } else { @@ -282,6 +256,8 @@ void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref br.done (); + } else if (test (skeys::log_key) || test (lkeys::log_key)) { + read_logs (xref); } else { skip_element (); } diff --git a/src/db/db/dbLayoutVsSchematicReader.h b/src/db/db/dbLayoutVsSchematicReader.h index 08aa9e5a1..7e0e98c36 100644 --- a/src/db/db/dbLayoutVsSchematicReader.h +++ b/src/db/db/dbLayoutVsSchematicReader.h @@ -28,6 +28,7 @@ #include "dbCell.h" #include "dbLayoutVsSchematic.h" #include "dbLayoutToNetlistReader.h" +#include "dbLog.h" #include "tlStream.h" namespace db { @@ -78,10 +79,8 @@ private: void read_netlist (db::LayoutVsSchematic *lvs); bool read_status (db::NetlistCrossReference::Status &status); - bool read_message (std::string &msg); void read_log_entry (db::NetlistCrossReference *xref); - void read_logs_for_circuits (db::NetlistCrossReference *xref); - bool read_severity (db::NetlistCrossReference::Severity &severity); + void read_logs (db::NetlistCrossReference *xref); void read_xref (db::NetlistCrossReference *xref); void read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b); void read_net_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b); diff --git a/src/db/db/dbLayoutVsSchematicWriter.cc b/src/db/db/dbLayoutVsSchematicWriter.cc index 9b1085102..9c100da4f 100644 --- a/src/db/db/dbLayoutVsSchematicWriter.cc +++ b/src/db/db/dbLayoutVsSchematicWriter.cc @@ -72,8 +72,6 @@ private: } std::string status_to_s (const db::NetlistCrossReference::Status status); - std::string severity_to_s (const db::NetlistCrossReference::Severity severity); - std::string message_to_s (const std::string &msg); void write (TokenizedOutput &stream, const db::NetlistCrossReference *xref); std::map > m_net2id_per_circuit_a, m_net2id_per_circuit_b; @@ -178,16 +176,6 @@ static std::string pin_id_to_s (const db::Pin *pin, const std::map -std::string std_writer_impl::message_to_s (const std::string &msg) -{ - if (msg.empty ()) { - return std::string (); - } else { - return Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")"; - } -} - template std::string std_writer_impl::status_to_s (const db::NetlistCrossReference::Status status) { @@ -206,30 +194,31 @@ std::string std_writer_impl::status_to_s (const db::NetlistCrossReference: } } -template -std::string std_writer_impl::severity_to_s (const db::NetlistCrossReference::Severity severity) -{ - if (severity == db::NetlistCrossReference::Info) { - return Keys::info_severity_key; - } else if (severity == db::NetlistCrossReference::Warning) { - return Keys::warning_severity_key; - } else if (severity == db::NetlistCrossReference::Error) { - return Keys::error_severity_key; - } else { - return std::string (); - } -} - template void std_writer_impl::write (TokenizedOutput &stream, const db::NetlistCrossReference *xref) { - for (db::NetlistCrossReference::circuits_iterator c = xref->begin_circuits (); c != xref->end_circuits (); ++c) { + if (! xref->other_log_entries ().empty ()) { + + TokenizedOutput o (stream, Keys::log_key); + o << endl; + + for (auto l = xref->other_log_entries ().begin (); l != xref->other_log_entries ().end (); ++l) { + { + TokenizedOutput to (o, Keys::log_entry_key, true); + this->write_log_entry (to, *l); + } + o << endl; + } + + } + + for (auto c = xref->begin_circuits (); c != xref->end_circuits (); ++c) { const db::NetlistCrossReference::PerCircuitData *pcd = xref->per_circuit_data_for (*c); tl_assert (pcd != 0); TokenizedOutput out (stream, Keys::circuit_key); - out << name_to_s (c->first) << name_to_s (c->second) << status_to_s (pcd->status) << message_to_s (pcd->msg); + out << name_to_s (c->first) << name_to_s (c->second) << status_to_s (pcd->status) << this->message_to_s (pcd->msg); out << endl; if (! pcd->log_entries.empty ()) { @@ -237,8 +226,11 @@ void std_writer_impl::write (TokenizedOutput &stream, const db::NetlistCro TokenizedOutput o (out, Keys::log_key); o << endl; - for (db::NetlistCrossReference::PerCircuitData::log_entries_const_iterator l = pcd->log_entries.begin (); l != pcd->log_entries.end (); ++l) { - TokenizedOutput (o, Keys::log_entry_key, true) << severity_to_s (l->severity) << message_to_s (l->msg); + for (auto l = pcd->log_entries.begin (); l != pcd->log_entries.end (); ++l) { + { + TokenizedOutput to (o, Keys::log_entry_key, true); + this->write_log_entry (to, *l); + } o << endl; } @@ -249,7 +241,7 @@ void std_writer_impl::write (TokenizedOutput &stream, const db::NetlistCro o << endl; for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) { - TokenizedOutput (o, Keys::net_key) << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << message_to_s (n->msg); + TokenizedOutput (o, Keys::net_key) << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << this->message_to_s (n->msg); } std::map pin2index_a, pin2index_b; @@ -257,15 +249,15 @@ void std_writer_impl::write (TokenizedOutput &stream, const db::NetlistCro build_pin_index_map (c->second, pin2index_b); for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) { - TokenizedOutput (o, Keys::pin_key) << pin_id_to_s (n->pair.first, pin2index_a) << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << message_to_s (n->msg); + TokenizedOutput (o, Keys::pin_key) << pin_id_to_s (n->pair.first, pin2index_a) << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << this->message_to_s (n->msg); } for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) { - TokenizedOutput (o, Keys::device_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg); + TokenizedOutput (o, Keys::device_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << this->message_to_s (n->msg); } for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) { - TokenizedOutput (o, Keys::circuit_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg); + TokenizedOutput (o, Keys::circuit_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << this->message_to_s (n->msg); } } diff --git a/src/db/db/dbLog.cc b/src/db/db/dbLog.cc new file mode 100644 index 000000000..acc295813 --- /dev/null +++ b/src/db/db/dbLog.cc @@ -0,0 +1,188 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 "dbCommon.h" +#include "dbLog.h" +#include "tlThreads.h" + +namespace db +{ + +// ------------------------------------------------------------------ +// A string repository for keeping the memory footprint low for +// the log entries + +class LogEntryStringRepository +{ +public: + LogEntryStringRepository () + { + // .. nothing yet .. + } + + size_t id_for_string (const std::string &s) + { + if (s.empty ()) { + return 0; + } + + tl::MutexLocker locker (&m_lock); + + auto m = m_id_to_string.find (s); + if (m == m_id_to_string.end ()) { + m_strings.push_back (s); + size_t id = m_strings.size (); + m_id_to_string.insert (std::make_pair (s, id)); + return id; + } else { + return m->second; + } + } + + const std::string &string_for_id (size_t id) const + { + if (id == 0) { + static const std::string empty; + return empty; + } + + tl::MutexLocker locker (&m_lock); + return m_strings [id - 1]; + } + +private: + mutable tl::Mutex m_lock; + std::vector m_strings; + std::map m_id_to_string; +}; + +static LogEntryStringRepository s_strings; + +// ------------------------------------------------------------------ +// LogEntryData implementation + +LogEntryData::LogEntryData () + : m_severity (NoSeverity), m_cell_name (0), m_message (0), m_category_name (0), m_category_description (0) +{ + // .. nothing yet .. +} + +LogEntryData::LogEntryData (Severity s, const std::string &msg) + : m_severity (s), m_cell_name (0), m_message (s_strings.id_for_string (msg)), m_category_name (0), m_category_description (0) +{ + // .. nothing yet .. +} + +LogEntryData::LogEntryData (Severity s, const std::string &cell_name, const std::string &msg) + : m_severity (s), m_cell_name (s_strings.id_for_string (cell_name)), m_message (s_strings.id_for_string (msg)), m_category_name (0), m_category_description (0) +{ + // .. nothing yet .. +} + +bool +LogEntryData::operator== (const LogEntryData &other) const +{ + return m_severity == other.m_severity && + m_message == other.m_message && + m_cell_name == other.m_cell_name && + m_geometry == other.m_geometry && + m_category_name == other.m_category_name && + m_category_description == other.m_category_description; +} + +const std::string & +LogEntryData::category_name () const +{ + return s_strings.string_for_id (m_category_name); +} + +void +LogEntryData::set_category_name (const std::string &s) +{ + m_category_name = s_strings.id_for_string (s); +} + +const std::string & +LogEntryData::category_description () const +{ + return s_strings.string_for_id (m_category_description); +} + +void +LogEntryData::set_category_description (const std::string &s) +{ + m_category_description = s_strings.id_for_string (s); +} + +const std::string & +LogEntryData::message () const +{ + return s_strings.string_for_id (m_message); +} + +void +LogEntryData::set_message (const std::string &n) +{ + m_message = s_strings.id_for_string (n); +} + +const std::string & +LogEntryData::cell_name () const +{ + return s_strings.string_for_id (m_cell_name); +} + +void +LogEntryData::set_cell_name (const std::string &n) +{ + m_cell_name = s_strings.id_for_string (n); +} + +std::string +LogEntryData::to_string (bool with_geometry) const +{ + std::string res; + + if (m_category_name != 0) { + if (m_category_description == 0) { + res += "[" + category_name () + "] "; + } else { + res += "[" + category_description () + "] "; + } + } + + if (m_cell_name != 0) { + res += tl::to_string (tr ("In cell ")); + res += cell_name (); + res += ": "; + } + + res += message (); + + if (with_geometry && ! m_geometry.box ().empty ()) { + res += tl::to_string (tr (", shape: ")) + m_geometry.to_string (); + } + + return res; +} + +} diff --git a/src/db/db/dbLog.h b/src/db/db/dbLog.h new file mode 100644 index 000000000..ff11f528e --- /dev/null +++ b/src/db/db/dbLog.h @@ -0,0 +1,178 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 + +*/ + +#ifndef _HDR_dbLog +#define _HDR_dbLog + +#include "dbCommon.h" +#include "dbPolygon.h" + +#include + +namespace db +{ + +/** + * @brief An enum describing the severity for a log entry + */ +enum Severity { + NoSeverity = 0, // unspecific + Info = 1, // information only + Warning = 2, // a warning + Error = 3 // an error +}; + +/** + * @brief A generic log entry + * + * This object can be used for collecting errors or warnings. + * It features a message and a severity level and optionally + * a polygon (for geometry marker), a category name and a category description. + */ +class DB_PUBLIC LogEntryData +{ +public: + typedef size_t string_id_type; + + /** + * @brief Creates a log entry + */ + LogEntryData (); + + /** + * @brief Creates a log entry with the severity and a message + */ + LogEntryData (Severity s, const std::string &msg); + + /** + * @brief Creates an error with the severity, a cell name and a message + */ + LogEntryData (Severity s, const std::string &cell_name, const std::string &msg); + + /** + * @brief Equality + */ + bool operator== (const LogEntryData &other) const; + + /** + * @brief Inequality + */ + bool operator!= (const LogEntryData &other) const + { + return ! operator== (other); + } + + /** + * @brief Sets the severity + */ + void set_severity (Severity severity) + { + m_severity = severity; + } + + /** + * @brief Gets the severity + */ + Severity severity () const + { + return m_severity; + } + + /** + * @brief The category name of the error + * Specifying the category name is optional. If a category is given, it will be used for + * the report. + */ + const std::string &category_name () const; + + /** + * @brief Sets the category name + */ + void set_category_name (const std::string &s); + + /** + * @brief The category description of the error + * Specifying the category description is optional. If a category is given, this attribute will + * be used for the category description. + */ + const std::string &category_description () const; + + /** + * @brief Sets the category description + */ + void set_category_description (const std::string &s); + + /** + * @brief Gets the geometry for this error + * Not all errors may specify a geometry. In this case, the polygon is empty. + */ + const db::DPolygon &geometry () const + { + return m_geometry; + } + + /** + * @brief Sets the geometry + */ + void set_geometry (const db::DPolygon &g) + { + m_geometry = g; + } + + /** + * @brief Gets the message for this error + */ + const std::string &message () const; + + /** + * @brief Sets the message + */ + void set_message (const std::string &n); + + /** + * @brief Gets the cell name the error occurred in + */ + const std::string &cell_name () const; + + /** + * @brief Sets the cell name + */ + void set_cell_name (const std::string &n); + + /** + * @brief Formats this message for printing + */ + std::string to_string (bool with_geometry = true) const; + +private: + Severity m_severity; + string_id_type m_cell_name; + string_id_type m_message; + db::DPolygon m_geometry; + string_id_type m_category_name, m_category_description; +}; + + + +} + +#endif diff --git a/src/db/db/dbNet.h b/src/db/db/dbNet.h index 99d3be607..d76e2b676 100644 --- a/src/db/db/dbNet.h +++ b/src/db/db/dbNet.h @@ -235,6 +235,7 @@ public: private: friend class Net; + friend class Circuit; size_t m_pin_id; Net *mp_net; @@ -246,6 +247,14 @@ private: { mp_net = net; } + + /** + * @brief Sets the pin ID + */ + void set_pin_id (size_t id) + { + m_pin_id = id; + } }; /** @@ -343,6 +352,7 @@ public: private: friend class Net; + friend class SubCircuit; size_t m_pin_id; SubCircuit *mp_subcircuit; @@ -355,6 +365,14 @@ private: { mp_net = net; } + + /** + * @brief Sets the pin ID + */ + void set_pin_id (size_t pin) + { + m_pin_id = pin; + } }; /** diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 0a1f2e60c..e628dcfb0 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -385,7 +385,7 @@ NetlistComparer::compare_impl (const db::Netlist *a, const db::Netlist *b) const std::string msg = generate_subcircuits_not_verified_warning (ca, verified_circuits_a, cb, verified_circuits_b); if (m_with_log) { - mp_logger->log_entry (db::NetlistCompareLogger::Error, msg); + mp_logger->log_entry (db::Error, msg); } mp_logger->circuit_skipped (ca, cb, msg); @@ -899,7 +899,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (mp_logger) { if (p->second && ! exact_match) { if (m_with_log) { - mp_logger->log_entry (db::NetlistCompareLogger::Error, + mp_logger->log_entry (db::Error, tl::sprintf (tl::to_string (tr ("Nets %s are paired explicitly, but are not identical topologically")), nets2string (p->first))); } mp_logger->net_mismatch (p->first.first, p->first.second); @@ -1145,12 +1145,12 @@ static void analyze_pin_mismatch (const db::Pin *pin1, const db::Circuit *c1, const db::Pin *pin2, const db::Circuit * /*c2*/, db::NetlistCompareLogger *logger) { if (! pin1) { - logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from reference netlist found in netlist.\nThis is an indication that a physical connection is not made to the subcircuit.")), pin2->expanded_name ())); + logger->log_entry (db::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from reference netlist found in netlist.\nThis is an indication that a physical connection is not made to the subcircuit.")), pin2->expanded_name ())); } if (! pin2) { - logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from netlist found in reference netlist.\nThis is an indication that additional physical connections are made to the subcircuit cell.")), pin1->expanded_name ())); + logger->log_entry (db::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from netlist found in reference netlist.\nThis is an indication that additional physical connections are made to the subcircuit cell.")), pin1->expanded_name ())); // attempt to identify pins which are creating invalid connections for (auto p = c1->begin_parents (); p != c1->end_parents (); ++p) { @@ -1159,7 +1159,7 @@ analyze_pin_mismatch (const db::Pin *pin1, const db::Circuit *c1, const db::Pin if (sc.circuit_ref () == c1) { const db::Net *net = sc.net_for_pin (pin1->id ()); if (net && (net->subcircuit_pin_count () > 1 || net->terminal_count () > 0 || net->pin_count () > 0)) { - logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (tl::to_string (tr ("Potential invalid connection in circuit %s, subcircuit cell reference at %s")), p->name (), sc.trans ().to_string ())); + logger->log_entry (db::Info, tl::sprintf (tl::to_string (tr ("Potential invalid connection in circuit %s, subcircuit cell reference at %s")), p->name (), sc.trans ().to_string ())); } } } diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index e5e2cfa7d..9c775982f 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -25,6 +25,7 @@ #include "dbCommon.h" #include "dbNetlist.h" +#include "dbLog.h" #include #include @@ -50,16 +51,6 @@ public: NetlistCompareLogger () { } virtual ~NetlistCompareLogger () { } - /** - * @brief An enum describing the severity for the log_entry function - */ - enum Severity { - NoSeverity = 0, // unspecific - Info = 1, // information only - Warning = 2, // a warning - Error = 3 // an error - }; - /** * @brief Begin logging for netlist a and b */ @@ -101,7 +92,7 @@ public: /** * @brief Receives log entries for the current circuit pair */ - virtual void log_entry (Severity /*level*/, const std::string & /*msg*/) { } + virtual void log_entry (db::Severity /*level*/, const std::string & /*msg*/) { } /** * @brief Nets a and b match exactly diff --git a/src/db/db/dbNetlistCompareCore.cc b/src/db/db/dbNetlistCompareCore.cc index 3d9e1907f..e4e61deea 100644 --- a/src/db/db/dbNetlistCompareCore.cc +++ b/src/db/db/dbNetlistCompareCore.cc @@ -1052,7 +1052,7 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange if (ambiguous) { if (logger) { if (with_log) { - logger->log_entry (db::NetlistCompareLogger::Warning, + logger->log_entry (db::Warning, tl::sprintf (tl::to_string (tr ("Matching nets %s from an ambiguous group of nets")), nets2string (p->first->net (), p->second->net ()))); } logger->match_ambiguous_nets (p->first->net (), p->second->net ()); @@ -1116,7 +1116,7 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange } if (logger && with_log && was_ambiguous) { - logger->log_entry (db::NetlistCompareLogger::Info, + logger->log_entry (db::Info, tl::sprintf (tl::to_string (tr ("Matching nets %s following an ambiguous match")), nets2string (n->net (), n_other->net ()))); } @@ -1397,7 +1397,7 @@ analyze_nodes_for_close_matches (const std::multimaplog_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg, + logger->log_entry (db::Info, tl::sprintf (msg, i->second->net ()->expanded_name (), j->second->net ()->expanded_name (), int (fuzz))); @@ -1417,7 +1417,7 @@ analyze_nodes_for_close_matches (const std::multimapsecond, *j->second, *k->second, g2); double fuzz_factor = double (fuzz) / i->first; if (fuzz_factor < max_fuzz_factor) { - logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg, + logger->log_entry (db::Info, tl::sprintf (msg, (layout2ref ? i : j)->second->net ()->expanded_name (), (layout2ref ? j : k)->second->net ()->expanded_name (), (layout2ref ? k : i)->second->net ()->expanded_name (), @@ -1493,7 +1493,7 @@ NetlistCompareCore::analyze_failed_matches () const } for (auto i = singular1.begin (); i != singular1.end (); ++i) { - logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("Net %s is not matching any net from reference netlist")), (*i)->net ()->expanded_name ())); + logger->log_entry (db::Error, tl::sprintf (tl::to_string (tr ("Net %s is not matching any net from reference netlist")), (*i)->net ()->expanded_name ())); } // attempt some analysis for close matches (including shorts / opens) @@ -1552,7 +1552,7 @@ NetlistCompareCore::derive_node_identities_from_node_set (std::vector::max() && depth > max_depth) { if (with_log) { - logger->log_entry (db::NetlistCompareLogger::Warning, tl::sprintf (tl::to_string (tr ("Maximum depth exhausted (max depth is %d)")), int (max_depth))); + logger->log_entry (db::Warning, tl::sprintf (tl::to_string (tr ("Maximum depth exhausted (max depth is %d)")), int (max_depth))); } if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) { tl::info << indent_s << "max. depth exhausted (" << depth << ">" << max_depth << ")"; @@ -1670,7 +1670,7 @@ NetlistCompareCore::derive_node_identities_from_node_set (std::vector::max () && double (std::max (nr->num1, nr->num2)) * double (n_branch) > double (max_n_branch)) { if (with_log) { - logger->log_entry (db::NetlistCompareLogger::Warning, tl::sprintf (tl::to_string (tr ("Maximum complexity exhausted (max complexity is %s, needs at least %s)")), tl::to_string (max_n_branch), tl::to_string (std::max (nr->num1, nr->num2) * n_branch))); + logger->log_entry (db::Warning, tl::sprintf (tl::to_string (tr ("Maximum complexity exhausted (max complexity is %s, needs at least %s)")), tl::to_string (max_n_branch), tl::to_string (std::max (nr->num1, nr->num2) * n_branch))); } if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) { tl::info << indent_s << "max. complexity exhausted (" << std::max (nr->num1, nr->num2) << "*" << n_branch << ">" << max_n_branch << ") - mismatch."; diff --git a/src/db/db/dbNetlistCompareUtils.cc b/src/db/db/dbNetlistCompareUtils.cc index 7320e6a58..18f4f4fb3 100644 --- a/src/db/db/dbNetlistCompareUtils.cc +++ b/src/db/db/dbNetlistCompareUtils.cc @@ -414,8 +414,8 @@ generic_categorizer::cat_for (const Obj *cls) } // explicit instantiations -template class DB_PUBLIC generic_categorizer; -template class DB_PUBLIC generic_categorizer; +template class generic_categorizer; +template class generic_categorizer; // -------------------------------------------------------------------------------------------------------------------- // DeviceCategorizer implementation diff --git a/src/db/db/dbNetlistCrossReference.cc b/src/db/db/dbNetlistCrossReference.cc index 93f46acea..37598df5c 100644 --- a/src/db/db/dbNetlistCrossReference.cc +++ b/src/db/db/dbNetlistCrossReference.cc @@ -27,6 +27,7 @@ namespace db { NetlistCrossReference::NetlistCrossReference () + : mp_per_circuit_data (0) { // .. nothing yet .. } diff --git a/src/db/db/dbNetlistCrossReference.h b/src/db/db/dbNetlistCrossReference.h index 71d32f7ca..aec52a8ed 100644 --- a/src/db/db/dbNetlistCrossReference.h +++ b/src/db/db/dbNetlistCrossReference.h @@ -105,15 +105,6 @@ public: std::string msg; }; - struct LogEntryData - { - LogEntryData (Severity s, const std::string &m) : severity (s), msg (m) { } - LogEntryData () : severity (NoSeverity) { } - - Severity severity; - std::string msg; - }; - struct PerCircuitData { PerCircuitData () : status (None) { } diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index a7c7d7b98..7d627695e 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -32,45 +32,6 @@ namespace db { -// ---------------------------------------------------------------------------------------- -// NetlistDeviceExtractorError implementation - -NetlistDeviceExtractorError::NetlistDeviceExtractorError () -{ - // .. nothing yet .. -} - -NetlistDeviceExtractorError::NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg) - : m_cell_name (cell_name), m_message (msg) -{ - // .. nothing yet .. -} - -std::string NetlistDeviceExtractorError::to_string () const -{ - std::string res; - - if (! m_category_name.empty ()) { - if (m_category_description.empty ()) { - res += "[" + m_category_name + "] "; - } else { - res += "[" + m_category_description + "] "; - } - } - - res += m_message; - - if (! m_cell_name.empty ()) { - res += tl::to_string (tr (", in cell: ")) + m_cell_name; - } - - if (! m_geometry.box ().empty ()) { - res += tl::to_string (tr (", shape: ")) + m_geometry.to_string (); - } - - return res; -} - // ---------------------------------------------------------------------------------------- // NetlistDeviceExtractor implementation @@ -586,43 +547,89 @@ std::string NetlistDeviceExtractor::cell_name () const void NetlistDeviceExtractor::error (const std::string &msg) { - m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); + m_log_entries.push_back (db::LogEntryData (db::Error, cell_name (), msg)); + m_log_entries.back ().set_category_name ("device-extract"); if (tl::verbosity () >= 20) { - tl::error << m_errors.back ().to_string (); + tl::error << m_log_entries.back ().to_string (); } } void NetlistDeviceExtractor::error (const std::string &msg, const db::DPolygon &poly) { - m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); - m_errors.back ().set_geometry (poly); + m_log_entries.push_back (db::LogEntryData (db::Error, cell_name (), msg)); + m_log_entries.back ().set_geometry (poly); + m_log_entries.back ().set_category_name ("device-extract"); if (tl::verbosity () >= 20) { - tl::error << m_errors.back ().to_string (); + tl::error << m_log_entries.back ().to_string (); } } void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg) { - m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); - m_errors.back ().set_category_name (category_name); - m_errors.back ().set_category_description (category_description); + m_log_entries.push_back (db::LogEntryData (db::Error, cell_name (), msg)); + m_log_entries.back ().set_category_name (category_name); + m_log_entries.back ().set_category_description (category_description); if (tl::verbosity () >= 20) { - tl::error << m_errors.back ().to_string (); + tl::error << m_log_entries.back ().to_string (); } } void NetlistDeviceExtractor::error (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly) { - m_errors.push_back (db::NetlistDeviceExtractorError (cell_name (), msg)); - m_errors.back ().set_category_name (category_name); - m_errors.back ().set_category_description (category_description); - m_errors.back ().set_geometry (poly); + m_log_entries.push_back (db::LogEntryData (db::Error, cell_name (), msg)); + m_log_entries.back ().set_category_name (category_name); + m_log_entries.back ().set_category_description (category_description); + m_log_entries.back ().set_geometry (poly); if (tl::verbosity () >= 20) { - tl::error << m_errors.back ().to_string (); + tl::error << m_log_entries.back ().to_string (); + } +} + +void NetlistDeviceExtractor::warn (const std::string &msg) +{ + m_log_entries.push_back (db::LogEntryData (db::Warning, cell_name (), msg)); + m_log_entries.back ().set_category_name ("device-extract"); + + if (tl::verbosity () >= 20) { + tl::warn << m_log_entries.back ().to_string (); + } +} + +void NetlistDeviceExtractor::warn (const std::string &msg, const db::DPolygon &poly) +{ + m_log_entries.push_back (db::LogEntryData (db::Warning, cell_name (), msg)); + m_log_entries.back ().set_geometry (poly); + m_log_entries.back ().set_category_name ("device-extract"); + + if (tl::verbosity () >= 20) { + tl::warn << m_log_entries.back ().to_string (); + } +} + +void NetlistDeviceExtractor::warn (const std::string &category_name, const std::string &category_description, const std::string &msg) +{ + m_log_entries.push_back (db::LogEntryData (db::Warning, cell_name (), msg)); + m_log_entries.back ().set_category_name (category_name); + m_log_entries.back ().set_category_description (category_description); + + if (tl::verbosity () >= 20) { + tl::warn << m_log_entries.back ().to_string (); + } +} + +void NetlistDeviceExtractor::warn (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly) +{ + m_log_entries.push_back (db::LogEntryData (db::Warning, cell_name (), msg)); + m_log_entries.back ().set_category_name (category_name); + m_log_entries.back ().set_category_description (category_description); + m_log_entries.back ().set_geometry (poly); + + if (tl::verbosity () >= 20) { + tl::warn << m_log_entries.back ().to_string (); } } diff --git a/src/db/db/dbNetlistDeviceExtractor.h b/src/db/db/dbNetlistDeviceExtractor.h index e1710c603..d12369f7d 100644 --- a/src/db/db/dbNetlistDeviceExtractor.h +++ b/src/db/db/dbNetlistDeviceExtractor.h @@ -30,127 +30,13 @@ #include "dbDeepShapeStore.h" #include "dbRegion.h" #include "dbNetShape.h" +#include "dbLog.h" #include "gsiObject.h" namespace db { -/** - * @brief An error object for the netlist device extractor - * - * The device extractor will keep errors using objects of this kind. - */ -class DB_PUBLIC NetlistDeviceExtractorError -{ -public: - /** - * @brief Creates an error - */ - NetlistDeviceExtractorError (); - - /** - * @brief Creates an error with a cell name and a message (the minimum information) - */ - NetlistDeviceExtractorError (const std::string &cell_name, const std::string &msg); - - /** - * @brief The category name of the error - * Specifying the category name is optional. If a category is given, it will be used for - * the report. - */ - const std::string &category_name () const - { - return m_category_name; - } - - /** - * @brief Sets the category name - */ - void set_category_name (const std::string &s) - { - m_category_name = s; - } - - /** - * @brief The category description of the error - * Specifying the category description is optional. If a category is given, this attribute will - * be used for the category description. - */ - const std::string &category_description () const - { - return m_category_description; - } - - /** - * @brief Sets the category description - */ - void set_category_description (const std::string &s) - { - m_category_description = s; - } - - /** - * @brief Gets the geometry for this error - * Not all errors may specify a geometry. In this case, the polygon is empty. - */ - const db::DPolygon &geometry () const - { - return m_geometry; - } - - /** - * @brief Sets the geometry - */ - void set_geometry (const db::DPolygon &g) - { - m_geometry = g; - } - - /** - * @brief Gets the message for this error - */ - const std::string &message () const - { - return m_message; - } - - /** - * @brief Sets the message - */ - void set_message (const std::string &n) - { - m_message = n; - } - - /** - * @brief Gets the cell name the error occurred in - */ - const std::string &cell_name () const - { - return m_cell_name; - } - - /** - * @brief Sets the cell name - */ - void set_cell_name (const std::string &n) - { - m_cell_name = n; - } - - /** - * @brief Formats this message for printing - */ - std::string to_string () const; - -private: - std::string m_cell_name; - std::string m_message; - db::DPolygon m_geometry; - std::string m_category_name, m_category_description; -}; - /** * @brief Specifies a single layer from the device extractor */ @@ -201,8 +87,8 @@ class DB_PUBLIC NetlistDeviceExtractor : public gsi::ObjectBase, public tl::Object { public: - typedef std::list error_list; - typedef error_list::const_iterator error_iterator; + typedef std::list log_entry_list; + typedef log_entry_list::const_iterator log_entry_iterator; typedef std::vector layer_definitions; typedef layer_definitions::const_iterator layer_definitions_iterator; typedef std::map input_layers; @@ -266,27 +152,27 @@ public: void extract (DeepShapeStore &dss, unsigned int layout_index, const input_layers &layers, Netlist &netlist, hier_clusters_type &clusters, double device_scaling = 1.0); /** - * @brief Gets the error iterator, begin + * @brief Clears the log entries */ - error_iterator begin_errors () + void clear_log_entries () { - return m_errors.begin (); + m_log_entries.clear (); } /** - * @brief Gets the error iterator, end + * @brief Gets the log entry iterator, begin */ - error_iterator end_errors () + log_entry_iterator begin_log_entries () { - return m_errors.end (); + return m_log_entries.begin (); } /** - * @brief Returns true, if there are errors + * @brief Gets the log entry iterator, end */ - bool has_errors () const + log_entry_iterator end_log_entries () { - return ! m_errors.empty (); + return m_log_entries.end (); } /** @@ -498,6 +384,42 @@ public: error (category_name, category_description, msg, poly.transformed (db::CplxTrans (dbu ()))); } + /** + * @brief Issues a warning with the given message + */ + void warn (const std::string &msg); + + /** + * @brief Issues a warning with the given message and warn shape + */ + void warn (const std::string &msg, const db::DPolygon &poly); + + /** + * @brief Issues a warning with the given message and warn shape + */ + void warn (const std::string &msg, const db::Polygon &poly) + { + warn (msg, poly.transformed (db::CplxTrans (dbu ()))); + } + + /** + * @brief Issues a warning with the given category name, description and message + */ + void warn (const std::string &category_name, const std::string &category_description, const std::string &msg); + + /** + * @brief Issues a warning with the given category name, description and message and warn shape + */ + void warn (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::DPolygon &poly); + + /** + * @brief Issues a warning with the given category name, description and message and warn shape + */ + void warn (const std::string &category_name, const std::string &category_description, const std::string &msg, const db::Polygon &poly) + { + warn (category_name, category_description, msg, poly.transformed (db::CplxTrans (dbu ()))); + } + /** * @brief Gets the name of the current cell */ @@ -556,7 +478,7 @@ private: std::string m_name; layer_definitions m_layer_definitions; std::vector m_layers; - error_list m_errors; + log_entry_list m_log_entries; std::map > m_new_devices; std::map > m_device_cells; diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index c6adfe34f..ba723fa19 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -136,11 +136,11 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vectorproperties_repository ().get_id_of_name (db::NetlistDeviceExtractor::device_id_property_name ()); // build an attribute equivalence map which lists the "attribute IDs" which are identical in terms of net names + // TODO: this feature is not really used as must-connect nets now are handled in the LayoutToNetlist class on netlist level. + // Remove this later. std::map > net_name_equivalence; if (m_text_annot_name_id.first) { diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index f85154378..3f8d8915b 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -106,6 +106,8 @@ public: * This is a glob expression rendering net names where partial nets with the * same name are joined even without explicit connection. * The cell-less version applies to top level cells only. + * NOTE: this feature is not really used as must-connect nets are handled now in the LayoutToNetlist extractor. + * Remove this function later. */ void set_joined_net_names (const std::list &jnn); @@ -113,6 +115,8 @@ public: * @brief Sets the joined net names attribute for a given cell name * While the single-parameter set_joined_net_names only acts on the top cell, this * version will act on the cell with the given name. + * NOTE: this feature is not really used as must-connect nets are handled now in the LayoutToNetlist extractor. + * Remove this function later. */ void set_joined_net_names (const std::string &cell_name, const std::list &jnn); @@ -122,11 +126,15 @@ public: * names that are to be connected. Multiple such groups can be specified. Each net name listed in a * group implies implicit joining of the corresponding labels into one net. * The cell-less version applies to top level cells only. + * NOTE: this feature is not really used as must-connect nets are handled now in the LayoutToNetlist extractor. + * Remove this function later. */ void set_joined_nets (const std::list > &jnn); /** * @brief Sets the joined nets attribute per cell + * NOTE: this feature is not really used as must-connect nets are handled now in the LayoutToNetlist extractor. + * Remove this function later. */ void set_joined_nets (const std::string &cell_name, const std::list > &jnn); diff --git a/src/db/db/dbSubCircuit.cc b/src/db/db/dbSubCircuit.cc index 58a607cf3..bd1047727 100644 --- a/src/db/db/dbSubCircuit.cc +++ b/src/db/db/dbSubCircuit.cc @@ -90,6 +90,24 @@ void SubCircuit::set_trans (const db::DCplxTrans &t) m_trans = t; } +void SubCircuit::erase_pin (size_t pin_id) +{ + Net *net = net_for_pin (pin_id); + + if (! tl::is_null_iterator (m_pin_refs [pin_id])) { + net->erase_subcircuit_pin (m_pin_refs [pin_id]); + } + + m_pin_refs.erase (m_pin_refs.begin () + pin_id); + + // correct pin IDs for the pins with ID > pin_id + for (auto p = m_pin_refs.begin () + pin_id; p != m_pin_refs.end (); ++p) { + if (! tl::is_null_iterator (*p)) { + (*p)->set_pin_id ((*p)->pin_id () - 1); + } + } +} + void SubCircuit::set_pin_ref_for_pin (size_t pin_id, Net::subcircuit_pin_iterator iter) { if (m_pin_refs.size () < pin_id + 1) { diff --git a/src/db/db/dbSubCircuit.h b/src/db/db/dbSubCircuit.h index a83bec881..618b8f230 100644 --- a/src/db/db/dbSubCircuit.h +++ b/src/db/db/dbSubCircuit.h @@ -230,6 +230,11 @@ private: */ void set_circuit_ref (Circuit *c); + /** + * @brief Erases the given pin reference + */ + void erase_pin (size_t pin_id); + /** * @brief Sets the circuit the subcircuit belongs to */ diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 2994880b9..f1abfe620 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -1839,7 +1839,7 @@ gsi::EnumIn decl_EdgesE gsi::enum_const ("OrthoDiagonalEdges", db::SpecialEdgeOrientationFilter::OrthoDiagonal, "@brief Diagonal or orthogonal edges are selected (0, 90, -45 and 45 degree)\n" ), - "@brief This enum specifies the the edge type for edge angle filters.\n" + "@brief This enum specifies the edge type for edge angle filters.\n" "\n" "This enum was introduced in version 0.28.\n" ); diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 417abd105..3d29e8e68 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -464,6 +464,20 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This attribute has been introduced in version 0.27.\n" ) + + gsi::method ("top_level_mode=", &db::LayoutToNetlist::set_top_level_mode, gsi::arg ("flag"), + "@brief Sets a flag indicating whether top level mode is enabled.\n" + "\n" + "In top level mode, must-connect warnings are turned into errors for example.\n" + "To enable top level mode, set this attribute to true. By default, top-level mode is turned off.\n" + "\n" + "This attribute has been introduced in version 0.28.13." + ) + + gsi::method ("top_level_mode", &db::LayoutToNetlist::top_level_mode, + "@brief Gets a flag indicating whether top level mode is enabled.\n" + "See \\top_level_mode= for details.\n" + "\n" + "This attribute has been introduced in version 0.28.13.\n" + ) + gsi::method ("clear_join_net_names", &db::LayoutToNetlist::clear_join_net_names, "@brief Clears all implicit net joining expressions.\n" "See \\extract_netlist for more details about this feature.\n" @@ -530,6 +544,11 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This method has been made parameter-less in version 0.27. Use \\include_floating_subcircuits= and \\join_net_names as substitutes for the arguments of previous versions." ) + + gsi::method ("check_extraction_errors", &db::LayoutToNetlist::check_extraction_errors, + "@brief Raises an exception if extraction errors are present\n" + "\n" + "This method has been introduced in version 0.28.13." + ) + gsi::method_ext ("internal_layout", &l2n_internal_layout, "@brief Gets the internal layout\n" "Usually it should not be required to obtain the internal layout. If you need to do so, make sure not to modify the layout as\n" @@ -707,6 +726,10 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@brief Reads the extracted netlist from the file.\n" "This method employs the native format of KLayout.\n" ) + + gsi::iterator ("each_log_entry|#each_error", &db::LayoutToNetlist::begin_log_entries, &db::LayoutToNetlist::end_log_entries, + "@brief Iterates over all log entries collected during device and netlist extraction.\n" + "This method has been introduced in version 0.28.13." + ) + gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"), "@brief Runs an antenna check on the extracted clusters\n" "\n" diff --git a/src/db/db/gsiDeclDbLog.cc b/src/db/db/gsiDeclDbLog.cc new file mode 100644 index 000000000..814178e7a --- /dev/null +++ b/src/db/db/gsiDeclDbLog.cc @@ -0,0 +1,117 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 "gsiDecl.h" +#include "gsiEnums.h" +#include "dbLog.h" + +namespace gsi +{ + +Class decl_dbNetlistDeviceExtractorError ("db", "LogEntryData", + gsi::method ("severity", &db::LogEntryData::severity, + "@brief Gets the severity attribute.\n" + ) + + gsi::method ("severity=", &db::LogEntryData::set_severity, gsi::arg ("severity"), + "@brief Sets the severity attribute.\n" + ) + + gsi::method ("message", &db::LogEntryData::message, + "@brief Gets the message text.\n" + ) + + gsi::method ("message=", &db::LogEntryData::set_message, gsi::arg ("message"), + "@brief Sets the message text.\n" + ) + + gsi::method ("cell_name", &db::LogEntryData::cell_name, + "@brief Gets the cell name.\n" + "See \\cell_name= for details about this attribute." + ) + + gsi::method ("cell_name=", &db::LogEntryData::set_cell_name, gsi::arg ("cell_name"), + "@brief Sets the cell name.\n" + "The cell (or circuit) name specifies the cell or circuit the " + "log entry is related to. If the log entry is an error or " + "warning generated during device extraction, the cell name is " + "the circuit the device should have appeared in." + ) + + gsi::method ("geometry", &db::LogEntryData::geometry, + "@brief Gets the geometry.\n" + "See \\geometry= for more details." + ) + + gsi::method ("geometry=", &db::LogEntryData::set_geometry, gsi::arg ("polygon"), + "@brief Sets the geometry.\n" + "The geometry is optional. If given, a marker may be shown when selecting this error." + ) + + gsi::method ("category_name", &db::LogEntryData::category_name, + "@brief Gets the category name.\n" + "See \\category_name= for more details." + ) + + gsi::method ("category_name=", &db::LogEntryData::set_category_name, gsi::arg ("name"), + "@brief Sets the category name.\n" + "The category name is optional. If given, it specifies a formal category name. Errors with the same " + "category name are shown in that category. If in addition a category description is specified " + "(see \\category_description), this description will be displayed as the title." + ) + + gsi::method ("category_description", &db::LogEntryData::category_description, + "@brief Gets the category description.\n" + "See \\category_name= for details about categories." + ) + + gsi::method ("category_description=", &db::LogEntryData::set_category_description, gsi::arg ("description"), + "@brief Sets the category description.\n" + "See \\category_name= for details about categories." + ) + + gsi::method ("to_s", &db::LogEntryData::to_string, gsi::arg ("with_geometry", true), + "@brief Gets the string representation of this error or warning.\n" + "This method has been introduced in version 0.28.13." + ), + "@brief A generic log entry\n" + "This class is used for example by the device extractor (see \\NetlistDeviceExtractor) to keep errors or warnings " + "that occurred during extraction of the devices.\n" + "\n" + "Other classes also make use of this object to store errors, warnings or information. " + "The log entry object features a severity (warning, error, info), a message, an optional " + "category name and description (good for filtering if needed) and an optional \\DPolygon object " + "for indicating some location or error marker." + "\n" + "The original class used to be \"NetlistDeviceExtractorError\" which had been introduced in version 0.26. " + "It was generalized and renamed in version 0.28.13 as it was basically not useful as a separate class." +); + +gsi::Enum decl_Severity ("db", "Severity", + gsi::enum_const ("NoSeverity", db::NoSeverity, + "@brief Specifies no particular severity (default)\n" + ) + + gsi::enum_const ("Warning", db::Warning, + "@brief Specifies warning severity (log with high priority, but do not stop)\n" + ) + + gsi::enum_const ("Error", db::Error, + "@brief Specifies error severity (preferred action is stop)\n" + ) + + gsi::enum_const ("Info", db::Info, + "@brief Specifies info severity (print if requested, otherwise silent)\n" + ), + "@brief This enum specifies the severity level for log entries.\n" + "\n" + "This enum was introduced in version 0.28.13.\n" +); + +gsi::ClassExt inject_SeverityEnum_into_LogEntryData (decl_Severity.defs ()); + +} diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index af209c40e..29f2e7396 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1659,7 +1659,8 @@ Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", "@brief Joins (connects) two nets into one\n" "This method will connect the 'with' net with 'net' and remove 'with'.\n" "\n" - "This method has been introduced in version 0.26.4." + "This method has been introduced in version 0.26.4. Starting with version 0.28.13, " + "net names will be formed from both input names, combining them with as a comma-separated list." ) + gsi::iterator ("each_net", (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::begin_nets, (db::Circuit::net_iterator (db::Circuit::*) ()) &db::Circuit::end_nets, "@brief Iterates over the nets of the circuit" diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index c8f1fe6a4..f138cdfb8 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -137,16 +137,16 @@ public: db::NetlistCompareLogger::circuit_mismatch (a, b, msg); } - virtual void log_entry (db::NetlistCompareLogger::Severity severity, const std::string &msg) + virtual void log_entry (db::Severity severity, const std::string &msg) { if (cb_log_entry.can_issue ()) { - cb_log_entry.issue (&GenericNetlistCompareLogger::log_entry, severity, msg); + cb_log_entry.issue (&GenericNetlistCompareLogger::log_entry, severity, msg); } else { db::NetlistCompareLogger::log_entry (severity, msg); } } - void log_entry_fb (db::NetlistCompareLogger::Severity severity, const std::string &msg) + void log_entry_fb (db::Severity severity, const std::string &msg) { db::NetlistCompareLogger::log_entry (severity, msg); } @@ -657,21 +657,7 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "This class has been introduced in version 0.26." ); -gsi::EnumIn decl_CompareLoggerSeverity ("db", "Severity", - gsi::enum_const ("NoSeverity", db::NetlistCompareLogger::NoSeverity, - "@brief Unspecific severity\n" - ) + - gsi::enum_const ("Info", db::NetlistCompareLogger::Info, - "@brief Information only\n" - ) + - gsi::enum_const ("Warning", db::NetlistCompareLogger::Warning, - "@brief A warning\n" - ) + - gsi::enum_const ("Error", db::NetlistCompareLogger::Error, - "@brief An error\n" - ), - "@brief This class represents the log severity level for \\GenericNetlistCompareLogger#log_entry.\n" - "This enum has been introduced in version 0.28." -); +extern gsi::Enum decl_Severity; +gsi::ClassExt inject_SeverityEnum_into_GenericNetlistCompareLogger (decl_Severity.defs ()); } diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 9219c823e..c7a214dca 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -156,64 +156,6 @@ Class decl_dbDeviceClassFactoryBase ("db", "DeviceClassF "This class has been introduced in version 0.27.3.\n" ); -Class decl_dbNetlistDeviceExtractorError ("db", "NetlistDeviceExtractorError", - gsi::method ("message", &db::NetlistDeviceExtractorError::message, - "@brief Gets the message text.\n" - ) + - gsi::method ("message=", &db::NetlistDeviceExtractorError::set_message, gsi::arg ("message"), - "@brief Sets the message text.\n" - ) + - gsi::method ("cell_name", &db::NetlistDeviceExtractorError::cell_name, - "@brief Gets the cell name.\n" - "See \\cell_name= for details about this attribute." - ) + - gsi::method ("cell_name=", &db::NetlistDeviceExtractorError::set_cell_name, gsi::arg ("cell_name"), - "@brief Sets the cell name.\n" - "The cell name is the name of the layout cell which was treated. This is " - "also the name of the circuit the device should have appeared in (it may be dropped because of this error). " - "If netlist hierarchy manipulation happens however, the circuit may not exist " - "any longer or may be renamed." - ) + - gsi::method ("geometry", &db::NetlistDeviceExtractorError::geometry, - "@brief Gets the geometry.\n" - "See \\geometry= for more details." - ) + - gsi::method ("geometry=", &db::NetlistDeviceExtractorError::set_geometry, gsi::arg ("polygon"), - "@brief Sets the geometry.\n" - "The geometry is optional. If given, a marker will be shown when selecting this error." - ) + - gsi::method ("category_name", &db::NetlistDeviceExtractorError::category_name, - "@brief Gets the category name.\n" - "See \\category_name= for more details." - ) + - gsi::method ("category_name=", &db::NetlistDeviceExtractorError::set_category_name, gsi::arg ("name"), - "@brief Sets the category name.\n" - "The category name is optional. If given, it specifies a formal category name. Errors with the same " - "category name are shown in that category. If in addition a category description is specified " - "(see \\category_description), this description will be displayed as the title of." - ) + - gsi::method ("category_description", &db::NetlistDeviceExtractorError::category_description, - "@brief Gets the category description.\n" - "See \\category_name= for details about categories." - ) + - gsi::method ("category_description=", &db::NetlistDeviceExtractorError::set_category_description, gsi::arg ("description"), - "@brief Sets the category description.\n" - "See \\category_name= for details about categories." - ), - "@brief An error that occurred during device extraction\n" - "The device extractor will keep errors that occurred during extraction of the devices. " - "It does not by using this error class.\n" - "\n" - "An error is basically described by the cell/circuit it occurs in and the message. " - "In addition, a geometry may be attached forming a marker that can be shown when the error is selected. " - "The geometry is given as a \\DPolygon object. If no geometry is specified, this polygon is empty.\n" - "\n" - "For categorization of the errors, a category name and description may be specified. If given, the " - "errors will be shown in the specified category. The category description is optional.\n" - "\n" - "This class has been introduced in version 0.26." -); - static const std::string &ld_name (const db::NetlistDeviceExtractorLayerDefinition *ld) { return ld->name; @@ -280,8 +222,10 @@ Class decl_dbNetlistDeviceExtractor ("db", "DeviceEx gsi::iterator ("each_layer_definition", &db::NetlistDeviceExtractor::begin_layer_definitions, &db::NetlistDeviceExtractor::end_layer_definitions, "@brief Iterates over all layer definitions." ) + - gsi::iterator ("each_error", &db::NetlistDeviceExtractor::begin_errors, &db::NetlistDeviceExtractor::end_errors, - "@brief Iterates over all errors collected in the device extractor." + gsi::iterator ("each_log_entry|#each_error", &db::NetlistDeviceExtractor::begin_log_entries, &db::NetlistDeviceExtractor::end_log_entries, + "@brief Iterates over all log entries collected in the device extractor." + "Starting with version 0.28.13, the preferred name of the method is 'each_log_entry' as " + "log entries have been generalized to become warnings too." ), "@brief The base class for all device extractors.\n" "This is an abstract base class for device extractors. See \\GenericDeviceExtractor for a generic " @@ -468,6 +412,36 @@ Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceE gsi::method ("error", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &GenericDeviceExtractor::error, gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), "@brief Issues an error with the given category name and description, message and database-unit polygon geometry\n" + ) + + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &)) &GenericDeviceExtractor::warn, + gsi::arg ("message"), + "@brief Issues a warning with the given message\n" + "Warnings have been introduced in version 0.28.13." + ) + + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::warn, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues a warning with the given message and micrometer-units polygon geometry\n" + "Warnings have been introduced in version 0.28.13." + ) + + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &, const db::Polygon &)) &GenericDeviceExtractor::warn, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues a warning with the given message and database-unit polygon geometry\n" + "Warnings have been introduced in version 0.28.13." + ) + + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &)) &GenericDeviceExtractor::warn, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), + "@brief Issues a warning with the given category name and description, message\n" + "Warnings have been introduced in version 0.28.13." + ) + + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::DPolygon &)) &GenericDeviceExtractor::warn, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues a warning with the given category name and description, message and micrometer-units polygon geometry\n" + "Warnings have been introduced in version 0.28.13." + ) + + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &, const db::Polygon &)) &GenericDeviceExtractor::warn, + gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues a warning with the given category name and description, message and database-unit polygon geometry\n" + "Warnings have been introduced in version 0.28.13." ), "@brief The basic class for implementing custom device extractors.\n" "\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 0d56d2dae..1fd37692f 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -497,3 +497,45 @@ TEST(5_ReaderFuture) compare_text_files (path, au_path); } +TEST(6_ReaderLog) +{ + db::LayoutToNetlist l2n; + + std::string in_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_6.l2n"); + tl::InputStream is_in (in_path); + + db::LayoutToNetlistStandardReader reader (is_in); + reader.read (&l2n); + + // verify against the input + + std::string path = tmp_file ("tmp.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_au_6.l2n"); + + compare_text_files (path, au_path); + + std::string in_path_s = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_6s.l2n"); + tl::InputStream is_in_s (in_path_s); + + l2n.clear_log_entries (); + db::LayoutToNetlistStandardReader reader_s (is_in_s); + reader_s.read (&l2n); + + // verify against the input + + path = tmp_file ("tmp2.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + compare_text_files (path, au_path); +} + diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index f2c1716e6..940173167 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -608,12 +608,12 @@ TEST(1_BasicExtraction) // do some probing after purging // top level - EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB"); - EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (0.0, 1.8))), "RINGO:FB,OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::Point (0, 1800))), "RINGO:FB,OSC"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal2, db::DPoint (-2.0, 1.8))), "(null)"); - EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB"); - EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:FB"); - EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VSS"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (-1.5, 1.8))), "RINGO:FB,OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (24.5, 1.8))), "RINGO:FB,OSC"); + EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (5.3, 0.0))), "RINGO:VDD,VSS"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (2.6, 1.0))), "RINGO:$I39"); EXPECT_EQ (qnet_name (l2n.probe_net (*rmetal1, db::DPoint (6.4, 1.0))), "RINGO:$I2"); @@ -2879,28 +2879,271 @@ TEST(11_DuplicateInstances) ); } +namespace +{ + +class TestRig +{ +public: + TestRig (db::Layout &ly) + : m_ly (ly) + { + nwell = define_layer (m_ly, m_lmap, 1); + nwell_lbl = define_layer (m_ly, m_lmap, 1, 1); + active = define_layer (m_ly, m_lmap, 2); + pplus = define_layer (m_ly, m_lmap, 10); + nplus = define_layer (m_ly, m_lmap, 11); + poly = define_layer (m_ly, m_lmap, 3); + poly_lbl = define_layer (m_ly, m_lmap, 3, 1); + diff_cont = define_layer (m_ly, m_lmap, 4); + poly_cont = define_layer (m_ly, m_lmap, 5); + metal1 = define_layer (m_ly, m_lmap, 6); + metal1_lbl = define_layer (m_ly, m_lmap, 6, 1); + via1 = define_layer (m_ly, m_lmap, 7); + metal2 = define_layer (m_ly, m_lmap, 8); + metal2_lbl = define_layer (m_ly, m_lmap, 8, 1); + } + + db::LayoutToNetlist * + make_l2n () + { + db::Cell &tc = m_ly.cell (*m_ly.begin_top_down ()); + + std::unique_ptr l2n_p (new db::LayoutToNetlist (db::RecursiveShapeIterator (m_ly, tc, std::set ()))); + db::LayoutToNetlist &l2n = *l2n_p; + + rbulk.reset (l2n.make_layer ("bulk")); + rnwell.reset (l2n.make_layer (nwell, "nwell")); + rnwell_lbl.reset (l2n.make_layer (nwell_lbl, "nwell_lbl")); + ractive.reset (l2n.make_layer (active, "active")); + rpplus.reset (l2n.make_layer (pplus, "pplus")); + rnplus.reset (l2n.make_layer (nplus, "nplus")); + rpoly.reset (l2n.make_polygon_layer (poly, "poly")); + rpoly_lbl.reset (l2n.make_text_layer (poly_lbl, "poly_lbl")); + rdiff_cont.reset (l2n.make_polygon_layer (diff_cont, "diff_cont")); + rpoly_cont.reset (l2n.make_polygon_layer (poly_cont, "poly_cont")); + rmetal1.reset (l2n.make_polygon_layer (metal1, "metal1")); + rmetal1_lbl.reset (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + rvia1.reset (l2n.make_polygon_layer (via1, "via1")); + rmetal2.reset (l2n.make_polygon_layer (metal2, "metal2")); + rmetal2_lbl.reset (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + ractive_in_nwell = *ractive & *rnwell; + rpactive = ractive_in_nwell & *rpplus; + rntie = ractive_in_nwell & *rnplus; + rpgate = rpactive & *rpoly; + rpsd = rpactive - rpgate; + + ractive_outside_nwell = *ractive - *rnwell; + rnactive = ractive_outside_nwell & *rnplus; + rptie = ractive_outside_nwell & *rpplus; + rngate = rnactive & *rpoly; + rnsd = rnactive - rngate; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = m_ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate + unsigned int lsd = m_ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain + unsigned int lpdiff = m_ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion + unsigned int lndiff = m_ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion + unsigned int lptie = m_ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie + unsigned int lntie = m_ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie + + rpgate.insert_into (&m_ly, tc.cell_index (), lgate); + rngate.insert_into (&m_ly, tc.cell_index (), lgate); + rpsd.insert_into (&m_ly, tc.cell_index (), lsd); + rnsd.insert_into (&m_ly, tc.cell_index (), lsd); + rpsd.insert_into (&m_ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&m_ly, tc.cell_index (), lndiff); + rpsd.insert_into (&m_ly, tc.cell_index (), lptie); + rnsd.insert_into (&m_ly, tc.cell_index (), lntie); + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rnwell.get (); + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + dl["W"] = rbulk.get (); + l2n.extract_devices (nmos_ex, dl); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + l2n.register_layer (rptie, "ptie"); + l2n.register_layer (rntie, "ntie"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rnwell); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + l2n.connect (rptie); + l2n.connect (rntie); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (rntie, *rnwell); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rdiff_cont, rptie); + l2n.connect (*rdiff_cont, rntie); + l2n.connect (*rnwell, rntie); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rnwell, *rnwell_lbl); // attaches labels + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + return l2n_p.release (); + } + + void dump_nets (db::LayoutToNetlist &l2n) + { + // debug layers produced for nets + // 201/0 -> Well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> N tie + // 213/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = m_ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = m_ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [&rptie ] = m_ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [&rntie ] = m_ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [rbulk.get () ] = m_ly.insert_layer (db::LayerProperties (214, 0)); + dump_map [rnwell.get () ] = m_ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [rpoly.get () ] = m_ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = m_ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = m_ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = m_ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = m_ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = m_ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::Cell &tc = m_ly.cell (*m_ly.begin_top_down ()); + db::CellMapping cm = l2n.cell_mapping_into (m_ly, tc); + dump_nets_to_layout (l2n, m_ly, dump_map, cm); + } + + void dump_nets_recursive (db::LayoutToNetlist &l2n) + { + // debug layers produced for nets + // 301/0 -> Well + // 303/0 -> Poly + // 304/0 -> Diffusion contacts + // 305/0 -> Poly contacts + // 306/0 -> Metal1 + // 307/0 -> Via1 + // 308/0 -> Metal2 + // 310/0 -> N source/drain + // 311/0 -> P source/drain + // 312/0 -> N tie + // 313/0 -> P tie + std::map dump_map; + dump_map [&rpsd ] = m_ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = m_ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [&rptie ] = m_ly.insert_layer (db::LayerProperties (312, 0)); + dump_map [&rntie ] = m_ly.insert_layer (db::LayerProperties (313, 0)); + dump_map [rbulk.get () ] = m_ly.insert_layer (db::LayerProperties (314, 0)); + dump_map [rnwell.get () ] = m_ly.insert_layer (db::LayerProperties (301, 0)); + dump_map [rpoly.get () ] = m_ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = m_ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = m_ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = m_ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = m_ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = m_ly.insert_layer (db::LayerProperties (308, 0)); + + // write nets to layout + db::Cell &tc = m_ly.cell (*m_ly.begin_top_down ()); + db::CellMapping cm = l2n.cell_mapping_into (m_ly, tc); + dump_recursive_nets_to_layout (l2n, m_ly, dump_map, cm); + } + + const db::LayerMap &lmap () const { return m_lmap; } + +private: + db::Layout &m_ly; + db::LayerMap m_lmap; + +public: + unsigned int nwell; + unsigned int nwell_lbl; + unsigned int active; + unsigned int pplus; + unsigned int nplus; + unsigned int poly; + unsigned int poly_lbl; + unsigned int diff_cont; + unsigned int poly_cont; + unsigned int metal1; + unsigned int metal1_lbl; + unsigned int via1; + unsigned int metal2; + unsigned int metal2_lbl; + std::unique_ptr rbulk; + std::unique_ptr rnwell; + std::unique_ptr rnwell_lbl; + std::unique_ptr ractive; + std::unique_ptr rpplus; + std::unique_ptr rnplus; + std::unique_ptr rpoly; + std::unique_ptr rpoly_lbl; + std::unique_ptr rdiff_cont; + std::unique_ptr rpoly_cont; + std::unique_ptr rmetal1; + std::unique_ptr rmetal1_lbl; + std::unique_ptr rvia1; + std::unique_ptr rmetal2; + std::unique_ptr rmetal2_lbl; + db::Region ractive_in_nwell; + db::Region rpactive; + db::Region rntie; + db::Region rpgate; + db::Region rpsd; + db::Region ractive_outside_nwell; + db::Region rnactive; + db::Region rptie; + db::Region rngate; + db::Region rnsd; +}; + +} + TEST(12_FlattenCircuitDoesFlattenLayout) { db::Layout ly; - db::LayerMap lmap; - - unsigned int nwell = define_layer (ly, lmap, 1); - unsigned int active = define_layer (ly, lmap, 2); - unsigned int pplus = define_layer (ly, lmap, 10); - unsigned int nplus = define_layer (ly, lmap, 11); - unsigned int poly = define_layer (ly, lmap, 3); - unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); - unsigned int diff_cont = define_layer (ly, lmap, 4); - unsigned int poly_cont = define_layer (ly, lmap, 5); - unsigned int metal1 = define_layer (ly, lmap, 6); - unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); - unsigned int via1 = define_layer (ly, lmap, 7); - unsigned int metal2 = define_layer (ly, lmap, 8); - unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + TestRig test_rig (ly); { db::LoadLayoutOptions options; - options.get_options ().layer_map = lmap; + options.get_options ().layer_map = test_rig.lmap (); options.get_options ().create_other_layers = false; std::string fn (tl::testdata ()); @@ -2912,172 +3155,32 @@ TEST(12_FlattenCircuitDoesFlattenLayout) reader.read (ly, options); } - db::Cell &tc = ly.cell (*ly.begin_top_down ()); - db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + std::unique_ptr l2n (test_rig.make_l2n ()); - std::unique_ptr rbulk (l2n.make_layer ("bulk")); - std::unique_ptr rnwell (l2n.make_layer (nwell, "nwell")); - std::unique_ptr ractive (l2n.make_layer (active, "active")); - std::unique_ptr rpplus (l2n.make_layer (pplus, "pplus")); - std::unique_ptr rnplus (l2n.make_layer (nplus, "nplus")); - std::unique_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); - std::unique_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); - std::unique_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); - std::unique_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); - std::unique_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); - std::unique_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); - std::unique_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); - std::unique_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); - std::unique_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); - - // derived regions - - db::Region ractive_in_nwell = *ractive & *rnwell; - db::Region rpactive = ractive_in_nwell & *rpplus; - db::Region rntie = ractive_in_nwell & *rnplus; - db::Region rpgate = rpactive & *rpoly; - db::Region rpsd = rpactive - rpgate; - - db::Region ractive_outside_nwell = *ractive - *rnwell; - db::Region rnactive = ractive_outside_nwell & *rnplus; - db::Region rptie = ractive_outside_nwell & *rpplus; - db::Region rngate = rnactive & *rpoly; - db::Region rnsd = rnactive - rngate; - - // return the computed layers into the original layout and write it for debugging purposes - - unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate - unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain - unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion - unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion - unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie - unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie - - rpgate.insert_into (&ly, tc.cell_index (), lgate); - rngate.insert_into (&ly, tc.cell_index (), lgate); - rpsd.insert_into (&ly, tc.cell_index (), lsd); - rnsd.insert_into (&ly, tc.cell_index (), lsd); - rpsd.insert_into (&ly, tc.cell_index (), lpdiff); - rnsd.insert_into (&ly, tc.cell_index (), lndiff); - rpsd.insert_into (&ly, tc.cell_index (), lptie); - rnsd.insert_into (&ly, tc.cell_index (), lntie); - - db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); - db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); - - // device extraction - - db::NetlistDeviceExtractor::input_layers dl; - - dl["SD"] = &rpsd; - dl["G"] = &rpgate; - dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes - dl["W"] = rnwell.get (); - l2n.extract_devices (pmos_ex, dl); - - dl["SD"] = &rnsd; - dl["G"] = &rngate; - dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes - dl["W"] = rbulk.get (); - l2n.extract_devices (nmos_ex, dl); - - // net extraction - - l2n.register_layer (rpsd, "psd"); - l2n.register_layer (rnsd, "nsd"); - l2n.register_layer (rptie, "ptie"); - l2n.register_layer (rntie, "ntie"); - - // Intra-layer - l2n.connect (rpsd); - l2n.connect (rnsd); - l2n.connect (*rnwell); - l2n.connect (*rpoly); - l2n.connect (*rdiff_cont); - l2n.connect (*rpoly_cont); - l2n.connect (*rmetal1); - l2n.connect (*rvia1); - l2n.connect (*rmetal2); - l2n.connect (rptie); - l2n.connect (rntie); - // Inter-layer - l2n.connect (rpsd, *rdiff_cont); - l2n.connect (rnsd, *rdiff_cont); - l2n.connect (*rpoly, *rpoly_cont); - l2n.connect (*rpoly_cont, *rmetal1); - l2n.connect (*rdiff_cont, *rmetal1); - l2n.connect (*rdiff_cont, rptie); - l2n.connect (*rdiff_cont, rntie); - l2n.connect (*rnwell, rntie); - l2n.connect (*rmetal1, *rvia1); - l2n.connect (*rvia1, *rmetal2); - l2n.connect (*rpoly, *rpoly_lbl); // attaches labels - l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels - l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels // Global - l2n.connect_global (rptie, "BULK"); - l2n.connect_global (*rbulk, "BULK"); + l2n->connect_global (test_rig.rptie, "BULK"); + l2n->connect_global (*test_rig.rbulk, "BULK"); + + // Extract with joining VSS and VDD + l2n->join_net_names (tl::GlobPattern ("{VSS,VDD}")); // create some mess - we have to keep references to the layers to make them not disappear - rmetal1_lbl.reset (0); - rmetal2_lbl.reset (0); - rpoly_lbl.reset (0); + test_rig.rmetal1_lbl.reset (0); + test_rig.rmetal2_lbl.reset (0); + test_rig.rpoly_lbl.reset (0); - l2n.extract_netlist (); + l2n->extract_netlist (); - l2n.netlist ()->flatten_circuit (l2n.netlist ()->circuit_by_name ("INV2")); - l2n.netlist ()->flatten_circuit (l2n.netlist ()->circuit_by_name ("INV2PAIR")); - l2n.netlist ()->flatten_circuit (l2n.netlist ()->circuit_by_name ("TRANS")); + l2n->netlist ()->flatten_circuit (l2n->netlist ()->circuit_by_name ("INV2")); + l2n->netlist ()->flatten_circuit (l2n->netlist ()->circuit_by_name ("INV2PAIR")); + l2n->netlist ()->flatten_circuit (l2n->netlist ()->circuit_by_name ("TRANS")); - // debug layers produced for nets - // 201/0 -> Well - // 203/0 -> Poly - // 204/0 -> Diffusion contacts - // 205/0 -> Poly contacts - // 206/0 -> Metal1 - // 207/0 -> Via1 - // 208/0 -> Metal2 - // 210/0 -> N source/drain - // 211/0 -> P source/drain - // 212/0 -> N tie - // 213/0 -> P tie - std::map dump_map; - dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); - dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); - dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); - dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); - dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); - dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); - dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); - dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); - dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); - dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); - dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); - dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); - - // write nets to layout - db::CellMapping cm = l2n.cell_mapping_into (ly, tc); - dump_nets_to_layout (l2n, ly, dump_map, cm); - - dump_map.clear (); - dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); - dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); - dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); - dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); - dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); - dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); - dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); - dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); - dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); - dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); - dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); - dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); - - dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + test_rig.dump_nets (*l2n); + test_rig.dump_nets_recursive (*l2n); // compare netlist as string CHECKPOINT (); - db::compare_netlist (_this, *l2n.netlist (), + db::compare_netlist (_this, *l2n->netlist (), "circuit RINGO ();\n" " device PMOS $1 (S=FB,G=$I7,D=VDD,B=VDD) (L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3);\n" " device PMOS $2 (S=VDD,G=$I7,D=FB,B=VDD) (L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55);\n" @@ -3134,25 +3237,11 @@ TEST(12_FlattenCircuitDoesFlattenLayout) TEST(13_JoinNetNames) { db::Layout ly; - db::LayerMap lmap; - - unsigned int nwell = define_layer (ly, lmap, 1); - unsigned int active = define_layer (ly, lmap, 2); - unsigned int pplus = define_layer (ly, lmap, 10); - unsigned int nplus = define_layer (ly, lmap, 11); - unsigned int poly = define_layer (ly, lmap, 3); - unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); - unsigned int diff_cont = define_layer (ly, lmap, 4); - unsigned int poly_cont = define_layer (ly, lmap, 5); - unsigned int metal1 = define_layer (ly, lmap, 6); - unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); - unsigned int via1 = define_layer (ly, lmap, 7); - unsigned int metal2 = define_layer (ly, lmap, 8); - unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + TestRig test_rig (ly); { db::LoadLayoutOptions options; - options.get_options ().layer_map = lmap; + options.get_options ().layer_map = test_rig.lmap (); options.get_options ().create_other_layers = false; std::string fn (tl::testdata ()); @@ -3164,167 +3253,25 @@ TEST(13_JoinNetNames) reader.read (ly, options); } - db::Cell &tc = ly.cell (*ly.begin_top_down ()); - db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + std::unique_ptr l2n (test_rig.make_l2n ()); - std::unique_ptr rbulk (l2n.make_layer ("bulk")); - std::unique_ptr rnwell (l2n.make_layer (nwell, "nwell")); - std::unique_ptr ractive (l2n.make_layer (active, "active")); - std::unique_ptr rpplus (l2n.make_layer (pplus, "pplus")); - std::unique_ptr rnplus (l2n.make_layer (nplus, "nplus")); - std::unique_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); - std::unique_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); - std::unique_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); - std::unique_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); - std::unique_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); - std::unique_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); - std::unique_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); - std::unique_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); - std::unique_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); - - // derived regions - - db::Region ractive_in_nwell = *ractive & *rnwell; - db::Region rpactive = ractive_in_nwell & *rpplus; - db::Region rntie = ractive_in_nwell & *rnplus; - db::Region rpgate = rpactive & *rpoly; - db::Region rpsd = rpactive - rpgate; - - db::Region ractive_outside_nwell = *ractive - *rnwell; - db::Region rnactive = ractive_outside_nwell & *rnplus; - db::Region rptie = ractive_outside_nwell & *rpplus; - db::Region rngate = rnactive & *rpoly; - db::Region rnsd = rnactive - rngate; - - // return the computed layers into the original layout and write it for debugging purposes - - unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate - unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain - unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion - unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion - unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie - unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie - - rpgate.insert_into (&ly, tc.cell_index (), lgate); - rngate.insert_into (&ly, tc.cell_index (), lgate); - rpsd.insert_into (&ly, tc.cell_index (), lsd); - rnsd.insert_into (&ly, tc.cell_index (), lsd); - rpsd.insert_into (&ly, tc.cell_index (), lpdiff); - rnsd.insert_into (&ly, tc.cell_index (), lndiff); - rpsd.insert_into (&ly, tc.cell_index (), lptie); - rnsd.insert_into (&ly, tc.cell_index (), lntie); - - db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); - db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); - - // device extraction - - db::NetlistDeviceExtractor::input_layers dl; - - dl["SD"] = &rpsd; - dl["G"] = &rpgate; - dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes - dl["W"] = rnwell.get (); - l2n.extract_devices (pmos_ex, dl); - - dl["SD"] = &rnsd; - dl["G"] = &rngate; - dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes - dl["W"] = rbulk.get (); - l2n.extract_devices (nmos_ex, dl); - - // net extraction - - l2n.register_layer (rpsd, "psd"); - l2n.register_layer (rnsd, "nsd"); - l2n.register_layer (rptie, "ptie"); - l2n.register_layer (rntie, "ntie"); - - // Intra-layer - l2n.connect (rpsd); - l2n.connect (rnsd); - l2n.connect (*rnwell); - l2n.connect (*rpoly); - l2n.connect (*rdiff_cont); - l2n.connect (*rpoly_cont); - l2n.connect (*rmetal1); - l2n.connect (*rvia1); - l2n.connect (*rmetal2); - l2n.connect (rptie); - l2n.connect (rntie); - // Inter-layer - l2n.connect (rpsd, *rdiff_cont); - l2n.connect (rnsd, *rdiff_cont); - l2n.connect (*rpoly, *rpoly_cont); - l2n.connect (*rpoly_cont, *rmetal1); - l2n.connect (*rdiff_cont, *rmetal1); - l2n.connect (*rdiff_cont, rptie); - l2n.connect (*rdiff_cont, rntie); - l2n.connect (*rnwell, rntie); - l2n.connect (*rmetal1, *rvia1); - l2n.connect (*rvia1, *rmetal2); - l2n.connect (*rpoly, *rpoly_lbl); // attaches labels - l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels - l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels // Global - l2n.connect_global (rntie, "VDD"); - l2n.connect_global (*rnwell, "VDD"); - l2n.connect_global (rptie, "VSS"); - l2n.connect_global (*rbulk, "VSS"); + l2n->connect_global (test_rig.rntie, "VDD"); + l2n->connect_global (*test_rig.rnwell, "VDD"); + l2n->connect_global (test_rig.rptie, "VSS"); + l2n->connect_global (*test_rig.rbulk, "VSS"); // Extract with joining VSS and VDD - l2n.join_net_names (tl::GlobPattern ("{VSS,VDD}")); - l2n.extract_netlist (); + l2n->join_net_names (tl::GlobPattern ("{VSS,VDD}")); - // debug layers produced for nets - // 201/0 -> Well - // 203/0 -> Poly - // 204/0 -> Diffusion contacts - // 205/0 -> Poly contacts - // 206/0 -> Metal1 - // 207/0 -> Via1 - // 208/0 -> Metal2 - // 210/0 -> N source/drain - // 211/0 -> P source/drain - // 212/0 -> N tie - // 213/0 -> P tie - std::map dump_map; - dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); - dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); - dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); - dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); - dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); - dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); - dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); - dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); - dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); - dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); - dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); - dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + l2n->extract_netlist (); - // write nets to layout - db::CellMapping cm = l2n.cell_mapping_into (ly, tc); - dump_nets_to_layout (l2n, ly, dump_map, cm); - - dump_map.clear (); - dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); - dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); - dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); - dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); - dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); - dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); - dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); - dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); - dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); - dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); - dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); - dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); - - dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + test_rig.dump_nets (*l2n); + test_rig.dump_nets_recursive (*l2n); // compare netlist as string CHECKPOINT (); - db::compare_netlist (_this, *l2n.netlist (), + db::compare_netlist (_this, *l2n->netlist (), "circuit RINGO ();\n" " subcircuit INV2 $1 (IN=$I7,$2=FB,OUT=OSC,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n" " subcircuit INV2 $2 (IN=FB,$2=$I34,OUT=$I17,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n" @@ -3359,30 +3306,14 @@ TEST(13_JoinNetNames) db::compare_layouts (_this, ly, au); } - TEST(14_JoinNets) { db::Layout ly; - db::LayerMap lmap; - - unsigned int nwell = define_layer (ly, lmap, 1); - unsigned int nwell_lbl = define_layer (ly, lmap, 1, 1); - unsigned int active = define_layer (ly, lmap, 2); - unsigned int pplus = define_layer (ly, lmap, 10); - unsigned int nplus = define_layer (ly, lmap, 11); - unsigned int poly = define_layer (ly, lmap, 3); - unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); - unsigned int diff_cont = define_layer (ly, lmap, 4); - unsigned int poly_cont = define_layer (ly, lmap, 5); - unsigned int metal1 = define_layer (ly, lmap, 6); - unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); - unsigned int via1 = define_layer (ly, lmap, 7); - unsigned int metal2 = define_layer (ly, lmap, 8); - unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + TestRig test_rig (ly); { db::LoadLayoutOptions options; - options.get_options ().layer_map = lmap; + options.get_options ().layer_map = test_rig.lmap (); options.get_options ().create_other_layers = false; std::string fn (tl::testdata ()); @@ -3394,181 +3325,35 @@ TEST(14_JoinNets) reader.read (ly, options); } - db::Cell &tc = ly.cell (*ly.begin_top_down ()); - db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + std::unique_ptr l2n (test_rig.make_l2n ()); - std::unique_ptr rbulk (l2n.make_layer ("bulk")); - std::unique_ptr rnwell (l2n.make_layer (nwell, "nwell")); - std::unique_ptr rnwell_lbl (l2n.make_layer (nwell_lbl, "nwell_lbl")); - std::unique_ptr ractive (l2n.make_layer (active, "active")); - std::unique_ptr rpplus (l2n.make_layer (pplus, "pplus")); - std::unique_ptr rnplus (l2n.make_layer (nplus, "nplus")); - std::unique_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); - std::unique_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); - std::unique_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); - std::unique_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); - std::unique_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); - std::unique_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); - std::unique_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); - std::unique_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); - std::unique_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); - - // derived regions - - db::Region ractive_in_nwell = *ractive & *rnwell; - db::Region rpactive = ractive_in_nwell & *rpplus; - db::Region rntie = ractive_in_nwell & *rnplus; - db::Region rpgate = rpactive & *rpoly; - db::Region rpsd = rpactive - rpgate; - - db::Region ractive_outside_nwell = *ractive - *rnwell; - db::Region rnactive = ractive_outside_nwell & *rnplus; - db::Region rptie = ractive_outside_nwell & *rpplus; - db::Region rngate = rnactive & *rpoly; - db::Region rnsd = rnactive - rngate; - - // return the computed layers into the original layout and write it for debugging purposes - - unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate - unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain - unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion - unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion - unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie - unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie - - rpgate.insert_into (&ly, tc.cell_index (), lgate); - rngate.insert_into (&ly, tc.cell_index (), lgate); - rpsd.insert_into (&ly, tc.cell_index (), lsd); - rnsd.insert_into (&ly, tc.cell_index (), lsd); - rpsd.insert_into (&ly, tc.cell_index (), lpdiff); - rnsd.insert_into (&ly, tc.cell_index (), lndiff); - rpsd.insert_into (&ly, tc.cell_index (), lptie); - rnsd.insert_into (&ly, tc.cell_index (), lntie); - - db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); - db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); - - // device extraction - - db::NetlistDeviceExtractor::input_layers dl; - - dl["SD"] = &rpsd; - dl["G"] = &rpgate; - dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes - dl["W"] = rnwell.get (); - l2n.extract_devices (pmos_ex, dl); - - dl["SD"] = &rnsd; - dl["G"] = &rngate; - dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes - dl["W"] = rbulk.get (); - l2n.extract_devices (nmos_ex, dl); - - // net extraction - - l2n.register_layer (rpsd, "psd"); - l2n.register_layer (rnsd, "nsd"); - l2n.register_layer (rptie, "ptie"); - l2n.register_layer (rntie, "ntie"); - - // Intra-layer - l2n.connect (rpsd); - l2n.connect (rnsd); - l2n.connect (*rnwell); - l2n.connect (*rpoly); - l2n.connect (*rdiff_cont); - l2n.connect (*rpoly_cont); - l2n.connect (*rmetal1); - l2n.connect (*rvia1); - l2n.connect (*rmetal2); - l2n.connect (rptie); - l2n.connect (rntie); - // Inter-layer - l2n.connect (rpsd, *rdiff_cont); - l2n.connect (rnsd, *rdiff_cont); - l2n.connect (rntie, *rnwell); - l2n.connect (*rpoly, *rpoly_cont); - l2n.connect (*rpoly_cont, *rmetal1); - l2n.connect (*rdiff_cont, *rmetal1); - l2n.connect (*rdiff_cont, rptie); - l2n.connect (*rdiff_cont, rntie); - l2n.connect (*rnwell, rntie); - l2n.connect (*rmetal1, *rvia1); - l2n.connect (*rvia1, *rmetal2); - l2n.connect (*rnwell, *rnwell_lbl); // attaches labels - l2n.connect (*rpoly, *rpoly_lbl); // attaches labels - l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels - l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels // Global - l2n.connect_global (*rbulk, "BULK"); - l2n.connect_global (rptie, "BULK"); + l2n->connect_global (*test_rig.rbulk, "BULK"); + l2n->connect_global (test_rig.rptie, "BULK"); // Extract while joining VSS with BULK and VDD with NWELL std::set jn; jn.insert ("VDD"); jn.insert ("NWELL"); - l2n.join_nets (tl::GlobPattern ("INV2"), jn); + l2n->join_nets (tl::GlobPattern ("INV2"), jn); jn.clear (); jn.insert ("VSS"); jn.insert ("BULK"); - l2n.join_nets (tl::GlobPattern ("INV2"), jn); + l2n->join_nets (tl::GlobPattern ("INV2"), jn); - // This will trigger an implicit connection on top level (side effect of explicit connections) + // Implicit connection of nets with same name "VDD" jn.clear (); jn.insert ("VDD"); - l2n.join_nets (jn); + l2n->join_nets (jn); - l2n.extract_netlist (); + l2n->extract_netlist (); - // debug layers produced for nets - // 201/0 -> Well - // 203/0 -> Poly - // 204/0 -> Diffusion contacts - // 205/0 -> Poly contacts - // 206/0 -> Metal1 - // 207/0 -> Via1 - // 208/0 -> Metal2 - // 210/0 -> N source/drain - // 211/0 -> P source/drain - // 212/0 -> N tie - // 213/0 -> P tie - std::map dump_map; - dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); - dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); - dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0)); - dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0)); - dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0)); - dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0)); - dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); - dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); - dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); - dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); - dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); - dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); - - // write nets to layout - db::CellMapping cm = l2n.cell_mapping_into (ly, tc); - dump_nets_to_layout (l2n, ly, dump_map, cm); - - dump_map.clear (); - dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); - dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); - dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0)); - dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0)); - dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0)); - dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0)); - dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); - dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); - dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); - dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); - dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); - dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); - - dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + test_rig.dump_nets (*l2n); + test_rig.dump_nets_recursive (*l2n); // compare netlist as string CHECKPOINT (); - db::compare_netlist (_this, *l2n.netlist (), + db::compare_netlist (_this, *l2n->netlist (), "circuit RINGO ();\n" " subcircuit INV2 $1 ('NWELL,VDD'=VDD,IN=$I15,$3=FB,OUT=OSC,VSS=VSS);\n" " subcircuit INV2 $2 ('NWELL,VDD'=VDD,IN=FB,$3=$I26,OUT=$I25,VSS=VSS);\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 68cac78b0..9acaab77d 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -449,3 +449,38 @@ TEST(2_WriterWithGlobalNets) db::compare_layouts (_this, ly2, au); } } + +TEST(3_Messages) +{ + db::Layout ly; + db::Cell &tc = ly.cell (ly.add_cell ("TOP")); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + l2n.extract_netlist (); + + l2n.log_entry (db::LogEntryData (db::Info, "info")); + l2n.log_entry (db::LogEntryData (db::Warning, "warning")); + l2n.log_entry (db::LogEntryData (db::Error, "error")); + + std::string path = tmp_file ("tmp_l2nwriter_3.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, false); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_writer_au_3.txt"); + + compare_text_files (path, au_path); + + path = tmp_file ("tmp_l2nwriter_3s.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, true); + writer.write (&l2n); + } + + au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_writer_au_3s.txt"); + + compare_text_files (path, au_path); +} diff --git a/src/db/unit_tests/dbLogTests.cc b/src/db/unit_tests/dbLogTests.cc new file mode 100644 index 000000000..03b2941f6 --- /dev/null +++ b/src/db/unit_tests/dbLogTests.cc @@ -0,0 +1,93 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 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 "dbLog.h" + +#include "tlUnitTest.h" + +TEST(1_Basic) +{ + db::LogEntryData data; + EXPECT_EQ (data.severity (), db::NoSeverity); + EXPECT_EQ (data.message (), std::string ()); + EXPECT_EQ (data.category_description (), std::string ()); + EXPECT_EQ (data.category_name (), std::string ()); + EXPECT_EQ (data.cell_name (), std::string ()); + EXPECT_EQ (data.geometry ().to_string (), "()"); + + EXPECT_EQ (data == db::LogEntryData (), true); + EXPECT_EQ (data != db::LogEntryData (), false); +} + +TEST(2_Attributes) +{ + db::LogEntryData data; + data.set_severity (db::Error); + data.set_message ("Message"); + data.set_category_name ("42"); + data.set_cell_name ("cell"); + data.set_category_description ("the answer"); + data.set_geometry (db::DPolygon (db::DBox (db::DPoint (1, 2), db::DPoint (3, 4)))); + + db::LogEntryData data2 = data; + + EXPECT_EQ (data == db::LogEntryData (), false); + EXPECT_EQ (data != db::LogEntryData (), true); + EXPECT_EQ (data == data2, true); + EXPECT_EQ (data != data2, false); + + EXPECT_EQ (data.severity (), db::Error); + EXPECT_EQ (data.message (), std::string ("Message")); + EXPECT_EQ (data.category_description (), std::string ("the answer")); + EXPECT_EQ (data.category_name (), std::string ("42")); + EXPECT_EQ (data.cell_name (), std::string ("cell")); + EXPECT_EQ (data.geometry ().to_string (), "(1,2;1,4;3,4;3,2)"); +} + +TEST(3_toString) +{ + db::LogEntryData data; + data.set_severity (db::Error); + data.set_message ("Message"); + data.set_category_name ("42"); + data.set_cell_name ("cell"); + data.set_category_description ("the answer"); + data.set_geometry (db::DPolygon (db::DBox (db::DPoint (1, 2), db::DPoint (3, 4)))); + + EXPECT_EQ (data.to_string (), std::string ("[the answer] In cell cell: Message, shape: (1,2;1,4;3,4;3,2)")); + + data.set_category_description (std::string ()); + + EXPECT_EQ (data.to_string (), std::string ("[42] In cell cell: Message, shape: (1,2;1,4;3,4;3,2)")); + + data.set_category_name (std::string ()); + + EXPECT_EQ (data.to_string (), std::string ("In cell cell: Message, shape: (1,2;1,4;3,4;3,2)")); + + data.set_geometry (db::DPolygon ()); + + EXPECT_EQ (data.to_string (), std::string ("In cell cell: Message")); + + data.set_cell_name (std::string ()); + + EXPECT_EQ (data.to_string (), std::string ("Message")); +} diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 0f1cc9a06..0eb1e4b6a 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -4698,14 +4698,14 @@ TEST(28_JoinSymmetricNets) " device PMOS $2 (S=VDD,G=A,D=OUT1) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" " device NMOS $3 (S=VSS,G=A,D=OUT1) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" " device NMOS $4 (S=VSS,G=A,D=OUT1) (L=0.25,W=1.5,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $5 (S=OUT2,G=A,D=VDD) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $6 (S=VDD,G=A,D=OUT2) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $7 (S=OUT2,G=A,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $8 (S=OUT2,G=A,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $9 (S=OUT2,G=A,D=VDD) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $10 (S=VDD,G=A,D=OUT2) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $11 (S=VSS,G=A,D=OUT2) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $12 (S=OUT2,G=A,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $5 (S='OUT2,OUT3',G=A,D=VDD) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $6 (S=VDD,G=A,D='OUT2,OUT3') (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $7 (S='OUT2,OUT3',G=A,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $8 (S='OUT2,OUT3',G=A,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $9 (S='OUT2,OUT3',G=A,D=VDD) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $10 (S=VDD,G=A,D='OUT2,OUT3') (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $11 (S=VSS,G=A,D='OUT2,OUT3') (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $12 (S='OUT2,OUT3',G=A,D=VSS) (L=0.25,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ) } diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index 5f370c062..cee4aa3de 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -2285,11 +2285,11 @@ TEST(50_SplitGatesSimple) EXPECT_EQ (nl2->to_string (), "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" - " device '' m11 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m11 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" // sd2 -> sd1 - " device '' m12 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m22 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); @@ -2314,10 +2314,10 @@ TEST(50_SplitGatesSimple) EXPECT_EQ (nl2->to_string (), "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" // sd1 -> sd2 - " device '' m11 (S=sd2,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m21 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m11 (S='sd1,sd2',G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); @@ -2389,10 +2389,10 @@ TEST(50_SplitGatesSimple) EXPECT_EQ (nl2->to_string (), "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" // sd1 -> sd2 - " device '' m11 (S=sd2,G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m21 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m11 (S='sd1,sd2',G=g1,D=n1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" @@ -2516,11 +2516,11 @@ TEST(51_SplitGatesStrict) EXPECT_EQ (nl2->to_string (), "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" - " device '' m11 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m21 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m11 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" // sd2 -> sd1 - " device '' m12 (S=n1,G=g1,D=sd1) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m22 (S=sd1,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); @@ -2623,10 +2623,10 @@ TEST(51_SplitGatesStrict) EXPECT_EQ (nl2->to_string (), "circuit '' (A=n1,B=n2,G1=g1,G2=g2);\n" // sd1 -> sd2 - " device '' m11 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m21 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m12 (S=n1,G=g1,D=sd2) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m22 (S=sd2,G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m11 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D='sd1,sd2') (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S='sd1,sd2',G=g2,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" " device '' mp1 (S=n1,G=g1,D=sd3) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" " device '' mp2 (S=sd3,G=g1,D=n2) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" @@ -2725,11 +2725,11 @@ TEST(52_SplitGatesMOS4) EXPECT_EQ (nl2->to_string (), "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" - " device '' m11 (S=n1,G=g1,D=sd1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m21 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m11 (S=n1,G=g1,D='sd1,sd2',B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S='sd1,sd2',G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" // sd2 -> sd1 - " device '' m12 (S=n1,G=g1,D=sd1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m22 (S=sd1,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D='sd1,sd2',B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S='sd1,sd2',G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); @@ -2754,10 +2754,10 @@ TEST(52_SplitGatesMOS4) EXPECT_EQ (nl2->to_string (), "circuit '' (A=n1,B=n2,G1=g1,G2=g2,VSS=vss);\n" // sd1 -> sd2 - " device '' m11 (S=sd2,G=g1,D=n1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m21 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m12 (S=n1,G=g1,D=sd2,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" - " device '' m22 (S=sd2,G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m11 (S='sd1,sd2',G=g1,D=n1,B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m21 (S='sd1,sd2',G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m12 (S=n1,G=g1,D='sd1,sd2',B=vss) (L=6,W=1,AS=0,AD=0,PS=0,PD=0);\n" + " device '' m22 (S='sd1,sd2',G=g2,D=n2,B=vss) (L=10,W=1,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index a3d4ac867..751d892c8 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -33,7 +33,7 @@ TEST(1_NetlistDeviceExtractorErrorBasic) { - db::NetlistDeviceExtractorError error; + db::LogEntryData error; EXPECT_EQ (error.message (), ""); error.set_message ("x"); @@ -47,12 +47,16 @@ TEST(1_NetlistDeviceExtractorErrorBasic) error.set_geometry (db::DPolygon (db::DBox (0, 1, 2, 3))); EXPECT_EQ (error.geometry ().to_string (), "(0,1;0,3;2,3;2,1)"); - error = db::NetlistDeviceExtractorError ("cell2", "msg2"); + error = db::LogEntryData (db::Error, "cell2", "msg2"); + EXPECT_EQ (error.severity () == db::Error, true); EXPECT_EQ (error.cell_name (), "cell2"); EXPECT_EQ (error.message (), "msg2"); EXPECT_EQ (error.category_name (), ""); EXPECT_EQ (error.category_description (), ""); EXPECT_EQ (error.geometry ().to_string (), "()"); + + error.set_severity (db::Warning); + EXPECT_EQ (error.severity () == db::Warning, true); } namespace { @@ -71,7 +75,7 @@ namespace { }; } -static std::string error2string (const db::NetlistDeviceExtractorError &e) +static std::string error2string (const db::LogEntryData &e) { return e.cell_name() + ":" + e.category_name () + ":" + e.category_description () + ":" + e.geometry ().to_string () + ":" + e.message (); @@ -81,12 +85,12 @@ TEST(2_NetlistDeviceExtractorErrors) { DummyDeviceExtractor dummy_ex; - EXPECT_EQ (dummy_ex.has_errors (), true); + EXPECT_EQ (dummy_ex.begin_log_entries () != dummy_ex.end_log_entries (), true); - std::vector errors (dummy_ex.begin_errors (), dummy_ex.end_errors ()); + std::vector errors (dummy_ex.begin_log_entries (), dummy_ex.end_log_entries ()); EXPECT_EQ (int (errors.size ()), 4); - EXPECT_EQ (error2string (errors [0]), ":::():msg1"); - EXPECT_EQ (error2string (errors [1]), ":::(0,1;0,3;2,3;2,1):msg2"); + EXPECT_EQ (error2string (errors [0]), ":device-extract::():msg1"); + EXPECT_EQ (error2string (errors [1]), ":device-extract::(0,1;0,3;2,3;2,1):msg2"); EXPECT_EQ (error2string (errors [2]), ":cat1:desc1:():msg1"); EXPECT_EQ (error2string (errors [3]), ":cat1:desc1:(10,11;10,13;12,13;12,11):msg3"); } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 3e13ac8df..093f428b9 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1380,15 +1380,15 @@ TEST(22_FlattenSubCircuitPinsJoinNets) nl2.flatten_circuit (nl2.circuit_by_name ("PTRANS")); EXPECT_EQ (nl2.to_string (), - "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" - " subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" - " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD='FB,VDD');\n" + " subcircuit INV2 INV2_SC1 (OUT=OSC,$2=VSS,IN='FB,VDD');\n" + " subcircuit INV2 INV2_SC2 (OUT='FB,VDD',$2=VSS,IN='FB,VDD');\n" "end;\n" - "circuit INV2 (IN=$5,$2=$5,OUT=OUT,$4=$4,$5=$5);\n" - " device PMOS $1 (S=$5,G=$5,D=$5) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $2 (S=$5,G=$5,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " subcircuit NTRANS SC2 ($1=$4,$2=$5,$3=$5);\n" - " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$5);\n" + "circuit INV2 (OUT=OUT,$2=$4,IN=IN);\n" + " device PMOS $1 (S=IN,G=IN,D=IN) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=IN,G=IN,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=IN,$3=IN);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=IN);\n" "end;\n" "circuit NTRANS ($1=$1,$2=$2,$3=$2);\n" " device NMOS $1 (S=$1,G=$2,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" @@ -1398,33 +1398,32 @@ TEST(22_FlattenSubCircuitPinsJoinNets) nl2.flatten_circuit (nl2.circuit_by_name ("NTRANS")); EXPECT_EQ (nl2.to_string (), - "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" - " subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" - " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "circuit RINGO (IN=IN,'OSC,VDD'='FB,OSC,VDD',VSS=VSS);\n" + " subcircuit INV2 INV2_SC1 ('IN,OUT'='FB,OSC,VDD',$2=VSS);\n" + " subcircuit INV2 INV2_SC2 ('IN,OUT'='FB,OSC,VDD',$2=VSS);\n" "end;\n" - "circuit INV2 (IN=OUT,$2=OUT,OUT=OUT,$4=$4,$5=OUT);\n" - " device PMOS $1 (S=OUT,G=OUT,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $2 (S=OUT,G=OUT,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $3 (S=$4,G=OUT,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $4 (S=$4,G=OUT,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "circuit INV2 ('IN,OUT'='IN,OUT',$2=$4);\n" + " device PMOS $1 (S='IN,OUT',G='IN,OUT',D='IN,OUT') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S='IN,OUT',G='IN,OUT',D='IN,OUT') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=$4,G='IN,OUT',D='IN,OUT') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$4,G='IN,OUT',D='IN,OUT') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); nl2.flatten_circuit (nl2.circuit_by_name ("INV2")); EXPECT_EQ (nl2.to_string (), - "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=OSC);\n" - " device PMOS $1 (S=OSC,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $2 (S=OSC,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $3 (S=VSS,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $4 (S=VSS,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $5 (S=OSC,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $6 (S=OSC,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $7 (S=VSS,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $8 (S=VSS,G=OSC,D=OSC) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "circuit RINGO (IN=IN,'OSC,VDD'='FB,OSC,VDD',VSS=VSS);\n" + " device PMOS $1 (S='FB,OSC,VDD',G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S='FB,OSC,VDD',G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=VSS,G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=VSS,G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $5 (S='FB,OSC,VDD',G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $6 (S='FB,OSC,VDD',G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $7 (S=VSS,G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $8 (S=VSS,G='FB,OSC,VDD',D='FB,OSC,VDD') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); - } TEST(23_BlankCircuit) @@ -1648,11 +1647,11 @@ TEST(25_JoinNets) c->join_nets (c->net_by_name ("IN"), c->net_by_name ("OUT")); EXPECT_EQ (nl.to_string (), - "circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5);\n" - " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" - " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" - " subcircuit PTRANS SC3 ($1=$5,$2=IN,$3=$2);\n" - " subcircuit NTRANS SC4 ($1=$4,$2=IN,$3=$2);\n" + "circuit INV2 ('IN,OUT'='IN,OUT',$2=$2,$3=$4,$4=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3='IN,OUT');\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3='IN,OUT');\n" + " subcircuit PTRANS SC3 ($1=$5,$2='IN,OUT',$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2='IN,OUT',$3=$2);\n" "end;\n" "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" " device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" @@ -1691,11 +1690,11 @@ TEST(26_JoinNets) c->join_nets (c->net_by_name ("IN"), c->net_by_name ("OUT")); EXPECT_EQ (nl.to_string (), - "circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5);\n" - " device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $2 (S=$5,G=$2,D=IN) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" - " device NMOS $4 (S=$4,G=$2,D=IN) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "circuit INV2 ('IN,OUT'='IN,OUT',$2=$2,$3=$4,$4=$5);\n" + " device PMOS $1 (S=$5,G='IN,OUT',D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=$5,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=$4,G='IN,OUT',D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$4,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" ); } diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 351bf8860..14a462810 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ dbCompoundOperationTests.cc \ dbFillToolTests.cc \ + dbLogTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionCheckUtilsTests.cc \ dbUtilsTests.cc \ diff --git a/src/doc/doc/manual/lvs_connect.xml b/src/doc/doc/manual/lvs_connect.xml index 5757e5336..501695d35 100644 --- a/src/doc/doc/manual/lvs_connect.xml +++ b/src/doc/doc/manual/lvs_connect.xml @@ -102,19 +102,21 @@ connect(metal2, metal2_labels)

Implicit connections can be useful to supply preliminary connections which are supposed to be created higher up in the hierarchy: - Imagine a circuit which a big power net for example. When the layout - is made, the power net may not be completely connected yet because the + Imagine a circuit with a big power net for example. When the layout + is made, the power net may not be completely connected yet, because the plan is to connect all parts of this power net later when the cell is integrated. In this situation, the - subcircuit cell itself won't be LVS clean because the power net is a single + subcircuit cell itself won't be LVS clean, because the power net is a single net schematic-wise, but exist as multiple nets layout-wise. This prevents bottom-up verification - a very useful technique to achieve LVS clean - layouts. + layouts. It also prevents matching in general, as the layout cell will + have two pins while the schematic subcircuit has only one. In this case, + the cell and subcircuit will never match.

To allow verification of such a cell, "implicit connections" can be - made by giving the net parts the same name through labels and assume + made by giving the net parts the same name through labels and assuming these parts are connected: for example to specify implicit connections between all parts of a "VDD" net, place a label "VDD" on each part and include the following statement @@ -126,9 +128,23 @@ connect(metal2, metal2_labels)

"connect_implicit" (see connect_implicit) can be present multiple times to make many of such connections. - Implicit connections will only be made on the topmost circuit to prevent false verification results. - Be careful not to use this option in a final verification of a full design as power net - opens may pass unnoticed. + Implicit connections are accepted on top level, but a warning is issued, indicating + that the connection needs to be made further up in the hierarchy. + In a subcircuit, implicit connections are required to be connected on the + next level of hierarchy - either physically or by another implicit connection. + This way, a missing physical connection does not escape and at least a warning + is issued if the connection is still not made on top level. +

+ +

+ You can declare the layout as being a top level one. This turns the + warning about missing physical connections into an error: +

+ +
top_level(true)
+ +

+ The "connect_implicit" feature is also called "must connect" nets in other systems.

@@ -142,7 +158,8 @@ connect(metal2, metal2_labels) This will connect all nets labelled with "VDD1" for example or those labelled with "VDD_5V". However, this statement will only connect "VDD1" with "VDD1", not nets with different labels. I.e. it will not connect "VDD1" with "VDD2" - labels. + labels. To make connections between differently named nets, use "explicit connections" + (see below).

@@ -151,30 +168,22 @@ connect(metal2, metal2_labels)

- The standard method "connect_implicit" will only act on top-level cells. - However, sometimes the construction of certain library cells requires - connecting nets inside subcells. For example, memory cells are often made - in a way that their common rails are exposed on different sides but - not connected internally. Formally, those cells need to be described by - circuits with multiple pins in the schematic. As the cells are only used - in certain contexts where these rails are connected, it's sufficient to - specify a single pin and connect the rails inside the subcells if labelled - properly. The following statement will connect all nets labelled with "VDD" + The above examples of "connect_implicit" apply to all cells. The statement + can be made cell specific, by giving a cell name glob pattern for the + first argument, followed by the net name pattern. +

+ +

+ The following statement will connect all nets labelled with "VDD" from the "MEMCELL" subcell:

connect_implicit("MEMCELL", "VDD")

- If MEMCELL is the top cell, the single-argument, unspecific "connect_implicit" - rule is applied, unless no such rule is given. In other words: the unspecific - rule has priority for the top cell. -

- -

- The cell argument can be a glob-style pattern. In this case, the rule is - applied to all matching cells. Again, the "connect_implicit" rule may be - given multiple times. In this case, all matching occurances act together. + The rule is applied to all cells matching the glob pattern in the first argument. + Again, the "connect_implicit" rule may be given multiple times. + In this case, all matching occurrences act together.

@@ -217,20 +226,18 @@ connect(metal2, metal2_labels)

- To align layout and schematics, bulk and VSS pins can be connected + To align layout and schematic, bulk and VSS pins can be connected explicitly. Same for n-well and VDD. - There is a certain risk to forget making these connections later. - But this risk can be mitigated by implementing DRC rules which - demand at least one tie-down diode for each isolated n-well island - or the bulk. + This scheme is similar to the "connect_implicit" scheme explained + above, but can connect differently named nets.

- To establish an explicit connection, make sure that n-well and + To establish an explicit connection in the above example, make sure that n-well and bulk have proper names. For the n-well this can be done by creating labels on the n-well islands giving them a proper name - e.g. "NWELL". The bulk isn't a real layout layer with polygons on it. Using "connect_global" - will both connect everthing on this layer and give it a name. + will both connect everything on this layer and give it a name.

@@ -242,17 +249,35 @@ connect(metal2, metal2_labels) connect_explicit("INV", [ "BULK", "VSS" ]) +

+ Note that this rule will form a new net called "BULK,VSS" combining both + subnets. +

+

The cell name can be a pattern. For example "INV*" will apply this rule on all cells starting with "INV". - The cell is not mandatory: if it is omitted, the rule is applied to top level only - to avoid introducing rules in subcells where they would mask layout errors. + The cell pattern is not mandatory: if it is omitted, the rule is applied to all + cells. +

+ +

+ Like implicit connections, explicit connections are checked for being made + on the next level of hierarchy, either physically or by another explicit or + implicit connection.

An explicit connection will also imply implicit connections on the nets listed in the net names. So in the example above, different pieces of "VSS" - are connected even if they are not physically connected. + are connected even if they are not physically connected. Again, it is checked + that these connections are made later up in the hierarchy. +

+ +

+ Again, the "connect_explicit" statements must be given before the netlist is + extracted. Typically this happens before or shortly after "connect" + statements.

diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 8aaa4341d..6cb79e63f 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -2277,6 +2277,18 @@ CODE # @synopsis device_scaling(factor) # See \Netter#device_scaling for a description of that function. + # %DRC% + # @name top_level + # @brief Specifies that the circuit is a chip top level circuit + # @synopsis top_level(flag) + # See \Netter#top_level for a description of that function. + + # %DRC% + # @name ignore_extraction_errors + # @brief Specifies whether to ignore extraction errors + # @synopsis ignore_extraction_errors(value) + # See \Netter#ignore_extraction_errors for a description of that function. + # %DRC% # @name extract_devices # @brief Extracts devices for a given device extractor and device layer selection @@ -2299,6 +2311,8 @@ CODE connect_implicit connect_explicit device_scaling + top_level + ignore_extraction_errors extract_devices l2n_data netlist diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index d56466183..99823336b 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -69,6 +69,8 @@ module DRC @l2n = nil @lnum = 0 @device_scaling = 1.0 + @ignore_extraction_errors = false + @top_level = false end # %DRC% @@ -234,6 +236,36 @@ module DRC @l2n && @l2n.device_scaling = factor end end + + # %DRC% + # @name top_level + # @brief Specifies top level mode + # @synopsis top_level(value) + # With this value set to false (the default), it is assumed that the + # circuit is not used as a top level chip circuit. In that case, for + # example must-connect nets which are not connected are reported as + # as warnings. If top level mode is set to true, such disconnected + # nets are reported as errors as this indicates a missing physical + # connection. + + def top_level(value) + @engine._context("top_level") do + @top_level = value + @l2n && @l2n.top_level_mode = value + end + end + + # %DRC% + # @name ignore_extraction_errors + # @brief Specifies whether to ignore extraction errors + # @synopsis ignore_extraction_errors(value) + # With this value set to false (the default), "extract_netlist" will raise + # an exception upon extraction errors. Otherwise, extraction errors will be logged + # but no error is raised. + + def ignore_extraction_errors(value) + @ignore_extraction_errors = value + end # %DRC% # @name clear_connections @@ -249,26 +281,31 @@ module DRC # %DRC% # @name connect_implicit - # @brief Specifies a search pattern for labels which create implicit net connections + # @brief Specifies a search pattern for implicit net connections ("must connect" nets) # @synopsis connect_implicit(label_pattern) # @synopsis connect_implicit(cell_pattern, label_pattern) - # Use this method to supply label strings which create implicit net connections - # on the top level circuit in the first version. This feature is useful to connect identically labelled nets - # while a component isn't integrated yet. If the component is integrated, nets may be connected - # on a higher hierarchy level - e.g. by a power mesh. Inside the component this net consists - # of individual islands. To properly perform netlist extraction and comparison, these islands - # need to be connected even though there isn't a physical connection. "connect_implicit" can - # achive this if these islands are labelled with the same text on the top level of the - # component. + # This method specifies a net name search pattern, either for all cells or for + # certain cells, given by a name search pattern. Search pattern follow the usual glob + # form (e.g. "A*" for all cells or nets with names starting with "A"). + # + # Then, for nets matching the net name pattern and for which there is more than + # one subnet, the subnets are connected. "Subnets" are physically disconnected parts + # of a net which carry the same name. # - # In the second version, the pattern can be specified for a cell range (given by a cell name pattern or a - # single cell name). These pattern are applied to non-top cells. The unspecific pattern - # has priority over the cell-specific ones. As the cell selector is a pattern itself, a - # single cell may fall into more than one category. In this case, the label filters are - # combined. + # This feature is useful for example for power nets which are complete in a cell, + # but are supposed to be connected upwards in the hierarchy ("must connect" nets). + # Physically there are multiple nets, logically - and specifically in the schematic for + # the purpose of LVS - there is only one net. + # "connect_implicit" now creates a virtual, combined physical net that matches the logical net. + # + # This is general bears the risk of missing a physical connection. The "connect_implicit" + # feature therefore checks whether the connection is made physically on the next hierarchy + # level, except for top-level cells for which it is assumed that this connection is made + # later. A warning is raised instead for top level cells. # # The implicit connections are applied on the next net extraction and cleared - # on "clear_connections". + # on "clear_connections". Another feature is \connect_explicit which allows connecting + # differently named subnets in a similar fashion. def connect_implicit(arg1, arg2 = nil) @@ -290,27 +327,24 @@ module DRC # %DRC% # @name connect_explicit - # @brief Specifies a list of net names for nets to connect explicitly + # @brief Specifies a list of net names for nets to connect ("must connect" nets) # @synopsis connect_explicit(net_names) # @synopsis connect_explicit(cell_pattern, net_names) # Use this method to explicitly connect nets even if there is no physical connection. - # As this breaks with the concept of physical verification, this feature should be used - # with care. + # The concept is similar to implicit connection (see \connect_implicit). The method gets + # a list of nets which are connected virtually, even if there is no physical connection. + # The first version applies this scheme to all cells, the second version to cells matching + # the cell name pattern. The cell name pattern follows the usual glob style form (e.g. "A*" + # applies the connection in all cells whose name starts with "A"). # - # The first version of this function will connect all nets listed in the "net_names" array - # in the top level cell. The second version takes a cell name pattern and connects all nets listed - # in "net_names" for cells matching this pattern. - # - # A use case for this method is the following: consider a set of standard cells. These do not have a bulk - # or n-well pin in the schematics. They also do not have build in tie-down diodes for the - # substrate connections. In this case there is a build-in discrepancy between the - # schematics and the layout: bulk and VSS are separate nets within the layout, but the - # schematic does not list them as separate. The solution is to make an explicit connection - # between VDD and n-well and VSS and bulk, provided VDD and VSS are properly labelled as "VDD" and "VSS" - # and n-well and bulk are accessible as named nets (for bulk you can use "connect_global"). - # - # The following code will establish an explicit connection for all cells called "INV.." between - # BULK and VSS nets: + # This method is useful to establish a logical connection which is made later up on the + # next level of hierarchy. For example, a standard cell my not contain substrate or well + # taps as these may be made by tap or spare cells. Logically however, the cell only has + # one power or ground pin for the devices and substrate or well. In order to match both + # representations - for example for the purpose of LVS - the dual power or ground pins have + # to be connected. Assuming that there is a global net "BULK" for the substrate and a + # net "VSS" for the sources of the NMOS devices, the following statement will create this + # connection for all cell names beginning with "INV": # # @code # connect_global(bulk, "BULK") @@ -318,14 +352,19 @@ module DRC # connect_explicit("INV*", [ "BULK", "VSS" ]) # @/code # + # The resulting net and pin will carry a name made from the combination of the connected + # nets. In this case it will be "BULK,VSS". + # + # The virtual connection in general bears the risk of missing a physical connection. + # The "connect_explicit" feature therefore checks whether the connection is made physically + # on the next hierarchy level ("must connect" nets), except for top-level cells for which + # it is assumed that this connection is made later. + # A warning is raised instead for top level cells. + # # Explicit connections also imply implicit connections between different parts of # one of the nets. In the example before, "VSS" pieces without a physical connection # will also be connected. # - # When you use explicit connections you should make sure by other ways that the connection - # is made physically. For example, for the bulk/n-well pin example above, by enforcing at least one - # tie-down diode per n-well island and in the substrate by means of a DRC rule. - # # The explicit connections are applied on the next net extraction and cleared # on "clear_connections". @@ -595,6 +634,11 @@ module DRC cfg.call(@l2n) end + # checks for errors if needed + if !@ignore_extraction_errors + @l2n.check_extraction_errors + end + end @l2n @@ -647,6 +691,7 @@ module DRC @layers = {} _make_data @l2n.device_scaling = @device_scaling + @l2n.top_level_mode = @top_level end end diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 565dbb24f..634e5546e 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -3074,7 +3074,7 @@ PartialService::partial_select (const db::DBox &box, lay::Editable::SelectionMod shape_flags |= db::ShapeIterator::Polygons; } if (edt::paths_enabled ()) { - // Note: points, edges and edge pairs don't have seperate entires, so + // Note: points, edges and edge pairs don't have separate entires, so // we count them as paths here shape_flags |= db::ShapeIterator::Paths; shape_flags |= db::ShapeIterator::Edges; diff --git a/src/layui/layui/layNetlistBrowserPage.cc b/src/layui/layui/layNetlistBrowserPage.cc index faf4d4000..e8c3947ed 100644 --- a/src/layui/layui/layNetlistBrowserPage.cc +++ b/src/layui/layui/layNetlistBrowserPage.cc @@ -40,6 +40,7 @@ #include "dbCellMapping.h" #include "dbLayerMapping.h" #include "dbCell.h" +#include "dbLog.h" #include #include @@ -792,6 +793,35 @@ NetlistBrowserPage::navigate_to (const QModelIndex &index, bool fwd) selection_changed (hierarchy_tree, directory_tree); } +void +NetlistBrowserPage::log_selection_changed () +{ + clear_highlights (); + + if (! mp_database.get () || ! mp_database->netlist ()) { + return; + } + + NetlistLogModel *model = dynamic_cast (log_view->model ()); + tl_assert (model != 0); + + QModelIndexList selection = log_view->selectionModel ()->selectedIndexes (); + for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) { + if (i->column () == 0) { + const db::LogEntryData *le = model->log_entry (*i); + if (le && le->geometry () != db::DPolygon () && ! le->cell_name ().empty ()) { + const db::Circuit *c = mp_database->netlist ()->circuit_by_name (le->cell_name ()); + if (c) { + m_markers.push_back (std::make_pair (c, le->geometry ())); + } + } + } + } + + update_highlights (); + adjust_view (); +} + void NetlistBrowserPage::add_to_history (const QModelIndex &index, bool fwd) { @@ -1132,12 +1162,18 @@ NetlistBrowserPage::setup_trees () db::LayoutToNetlist *l2ndb = mp_database.get (); db::LayoutVsSchematic *lvsdb = dynamic_cast (l2ndb); - if (lvsdb && lvsdb->cross_ref ()) { + QIcon log_tab_icon; - NetlistLogModel *new_model = new NetlistLogModel (log_view, lvsdb->cross_ref ()); + if ((lvsdb && lvsdb->cross_ref ()) || (l2ndb && ! l2ndb->log_entries ().empty ())) { + + NetlistLogModel *new_model = new NetlistLogModel (log_view, lvsdb->cross_ref (), l2ndb); delete log_view->model (); log_view->setModel (new_model); + connect (log_view->selectionModel (), SIGNAL (selectionChanged (const QItemSelection &, const QItemSelection &)), this, SLOT (log_selection_changed ())); + + log_tab_icon = NetlistLogModel::icon_for_severity (new_model->max_severity ()); + } else { delete log_view->model (); @@ -1145,6 +1181,8 @@ NetlistBrowserPage::setup_trees () } + mode_tab->setTabIcon (3, log_tab_icon); + { // NOTE: with the tree as the parent, the tree will take over ownership of the model NetlistBrowserModel *new_model = new NetlistBrowserModel (nl_directory_tree, l2ndb, &m_colorizer); @@ -1244,6 +1282,7 @@ NetlistBrowserPage::clear_highlights () { m_current_path = lay::NetlistObjectsPath (); m_selected_paths.clear (); + m_markers.clear (); update_highlights (); } @@ -1403,6 +1442,17 @@ NetlistBrowserPage::adjust_view () } + // add markers boxes + + for (auto marker = m_markers.begin (); marker != m_markers.end (); ++marker) { + + std::pair tr = trans_for (marker->first, *layout, *cell, m_cell_context_cache, cv.context_dtrans ()); + if (tr.first) { + bbox += (tr.second * marker->second).box (); + } + + } + if (! bbox.empty ()) { std::vector tv = mp_view->cv_transform_variants (m_cv_index); @@ -1644,10 +1694,12 @@ NetlistBrowserPage::update_highlights () } } + std::vector tv = mp_view->cv_transform_variants (m_cv_index); + size_t n_markers = 0; bool not_all_shapes_are_shown = false; - for (std::vector::const_iterator path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) { + for (auto path = m_selected_paths.begin (); path != m_selected_paths.end (); ++path) { const db::Circuit *circuit = path->root.first; if (! circuit) { @@ -1679,7 +1731,6 @@ NetlistBrowserPage::update_highlights () // a map of display properties vs. layer properties // correct DBU differences between the storage layout and the original layout - std::vector tv = mp_view->cv_transform_variants (m_cv_index); for (std::vector::iterator t = tv.begin (); t != tv.end (); ++t) { *t = *t * trans * db::DCplxTrans (layout->dbu () / original_layout.dbu ()); } @@ -1700,6 +1751,28 @@ NetlistBrowserPage::update_highlights () } + for (auto marker = m_markers.begin (); marker != m_markers.end (); ++marker) { + + // computes the transformation supplied by the path + + std::pair tr = trans_for (marker->first, *layout, *cell, m_cell_context_cache, cv.context_dtrans ()); + if (! tr.first) { + continue; + } + + // creates a highlight from the marker + + tl::Color color = make_valid_color (m_colorizer.marker_color ()); + + mp_markers.push_back (new lay::Marker (mp_view, m_cv_index)); + mp_markers.back ()->set (marker->second, db::DCplxTrans (1.0 / original_layout.dbu ()) * tr.second, tv); + mp_markers.back ()->set_color (color); + mp_markers.back ()->set_frame_color (color); + + configure_marker (mp_markers.back (), true); + + } + if (not_all_shapes_are_shown) { info_label->setText (tl::to_qstring ("

" + tl::to_string (QObject::tr ("Not all shapes are highlighted")) + diff --git a/src/layui/layui/layNetlistBrowserPage.h b/src/layui/layui/layNetlistBrowserPage.h index 258385dd2..ad29cdba9 100644 --- a/src/layui/layui/layNetlistBrowserPage.h +++ b/src/layui/layui/layNetlistBrowserPage.h @@ -211,6 +211,7 @@ private slots: void nl_selection_changed (); void sch_selection_changed (); void xref_selection_changed (); + void log_selection_changed (); void browse_color_for_net (); void select_color_for_net (); @@ -243,6 +244,7 @@ private: bool m_update_needed; lay::NetlistObjectsPath m_current_path; std::vector m_selected_paths; + std::vector > m_markers; lay::NetInfoDialog *mp_info_dialog; tl::DeferredMethod dm_update_highlights; tl::DeferredMethod dm_rerun_macro; diff --git a/src/layui/layui/layNetlistLogModel.cc b/src/layui/layui/layNetlistLogModel.cc index aed4ac6f4..82784d628 100644 --- a/src/layui/layui/layNetlistLogModel.cc +++ b/src/layui/layui/layNetlistLogModel.cc @@ -78,20 +78,37 @@ namespace { const std::string var_sep (" \u21D4 "); -NetlistLogModel::NetlistLogModel (QWidget *parent, const db::NetlistCrossReference *cross_ref) - : QAbstractItemModel (parent) +NetlistLogModel::NetlistLogModel (QWidget *parent, const db::NetlistCrossReference *cross_ref, const db::LayoutToNetlist *l2n) + : QAbstractItemModel (parent), m_max_severity (db::NoSeverity) { - tl_assert (cross_ref->netlist_a () != 0); - tl_assert (cross_ref->netlist_b () != 0); + tl_assert (! cross_ref || cross_ref->netlist_a () != 0); + tl_assert (! cross_ref || cross_ref->netlist_b () != 0); - if (! cross_ref->other_log_entries ().empty ()) { - m_circuits.push_back (std::make_pair (std::make_pair ((const db::Circuit *)0, (const db::Circuit *)0), &cross_ref->other_log_entries ())); + mp_lvsdb_messages = cross_ref ? &cross_ref->other_log_entries () : 0; + if (mp_lvsdb_messages) { + for (auto l = mp_lvsdb_messages->begin (); l != mp_lvsdb_messages->end (); ++l) { + m_max_severity = std::max (m_max_severity, l->severity ()); + } } - for (auto i = cross_ref->begin_circuits (); i != cross_ref->end_circuits (); ++i) { - const db::NetlistCrossReference::PerCircuitData *pcd = cross_ref->per_circuit_data_for (*i); - if (pcd && i->first && i->second && ! pcd->log_entries.empty ()) { - m_circuits.push_back (std::make_pair (*i, &pcd->log_entries)); + mp_l2n_messages = l2n ? &l2n->log_entries () : 0; + if (mp_l2n_messages) { + for (auto l = mp_l2n_messages->begin (); l != mp_l2n_messages->end (); ++l) { + m_max_severity = std::max (m_max_severity, l->severity ()); + } + } + + m_global_entries = int ((mp_lvsdb_messages ? mp_lvsdb_messages->size () : 0) + (mp_l2n_messages ? mp_l2n_messages->size () : 0)); + + if (cross_ref) { + for (auto i = cross_ref->begin_circuits (); i != cross_ref->end_circuits (); ++i) { + const db::NetlistCrossReference::PerCircuitData *pcd = cross_ref->per_circuit_data_for (*i); + if (pcd && (i->first || i->second) && ! pcd->log_entries.empty ()) { + for (auto l = pcd->log_entries.begin (); l != pcd->log_entries.end (); ++l) { + m_max_severity = std::max (m_max_severity, l->severity ()); + } + m_circuits.push_back (std::make_pair (*i, &pcd->log_entries)); + } } } @@ -102,9 +119,11 @@ bool NetlistLogModel::hasChildren (const QModelIndex &parent) const { if (! parent.isValid ()) { - return ! m_circuits.empty (); + return m_global_entries > 0 || ! m_circuits.empty (); + } else if (! parent.parent ().isValid ()) { + return parent.row () >= m_global_entries; } else { - return ! parent.parent ().isValid (); + return false; } } @@ -114,7 +133,7 @@ NetlistLogModel::index (int row, int column, const QModelIndex &parent) const if (! parent.isValid ()) { return createIndex (row, column, (void *) (0)); } else { - return createIndex (row, column, (void *) (& m_circuits [parent.row ()])); + return createIndex (row, column, (void *) (& m_circuits [parent.row () - m_global_entries])); } } @@ -125,7 +144,7 @@ NetlistLogModel::parent (const QModelIndex &child) const return QModelIndex (); } else { const circuit_entry *ce = (const circuit_entry *) child.internalPointer (); - return createIndex (int (ce - & m_circuits.front ()), child.column (), (void *) (0)); + return createIndex (int (ce - & m_circuits.front ()) + m_global_entries, child.column (), (void *) (0)); } } @@ -133,11 +152,11 @@ int NetlistLogModel::rowCount (const QModelIndex &parent) const { if (! parent.isValid ()) { - return int (m_circuits.size ()); + return int (m_circuits.size ()) + m_global_entries; } else if (parent.parent ().isValid ()) { return 0; - } else if (parent.row () >= 0 && parent.row () < int (m_circuits.size ())) { - return int (m_circuits [parent.row ()].second->size ()); + } else if (parent.row () >= m_global_entries && parent.row () < int (m_circuits.size ()) + m_global_entries) { + return int (m_circuits [parent.row () - m_global_entries].second->size ()); } else { return 0; } @@ -149,39 +168,63 @@ NetlistLogModel::columnCount (const QModelIndex & /*parent*/) const return 1; } -QVariant -NetlistLogModel::data (const QModelIndex &index, int role) const +QIcon +NetlistLogModel::icon_for_severity (db::Severity severity) { - const db::NetlistCrossReference::LogEntryData *le = 0; + if (severity == db::Error) { + return QIcon (QString::fromUtf8 (":/error_16px.png")); + } else if (severity == db::Warning) { + return QIcon (QString::fromUtf8 (":/warn_16px.png")); + } else if (severity == db::Info) { + return QIcon (QString::fromUtf8 (":/info_16px.png")); + } else { + return QIcon (); + } +} + +const db::LogEntryData * +NetlistLogModel::log_entry (const QModelIndex &index) const +{ + const db::LogEntryData *le = 0; + if (index.parent ().isValid ()) { const circuit_entry *ce = (const circuit_entry *) index.internalPointer (); if (ce) { - le = &(*ce->second) [index.row ()]; + le = (ce->second->begin () + index.row ()).operator-> (); + } + } else if (index.row () < m_global_entries) { + int n_l2n = int (mp_l2n_messages ? mp_l2n_messages->size () : 0); + if (index.row () < n_l2n) { + le = (mp_l2n_messages->begin () + index.row ()).operator-> (); + } else { + le = (mp_lvsdb_messages->begin () + (index.row () - n_l2n)).operator-> (); } } + return le; +} + +QVariant +NetlistLogModel::data (const QModelIndex &index, int role) const +{ + const db::LogEntryData *le = log_entry (index); + if (role == Qt::DecorationRole) { - if (! le) { - // ignore - } else if (le->severity == db::NetlistCrossReference::Error) { - return QIcon (QString::fromUtf8 (":/error_16px.png")); - } else if (le->severity == db::NetlistCrossReference::Warning) { - return QIcon (QString::fromUtf8 (":/warn_16px.png")); - } else if (le->severity == db::NetlistCrossReference::Info) { - return QIcon (QString::fromUtf8 (":/info_16px.png")); + if (le) { + return icon_for_severity (le->severity ()); } } else if (role == Qt::DisplayRole) { - if (index.parent ().isValid ()) { - if (le) { - return QVariant (tl::to_qstring (le->msg)); - } - } else if (index.row () >= 0 && index.row () < int (m_circuits.size ())) { - const std::pair &cp = m_circuits [index.row ()].first; - if (! cp.first || ! cp.second) { - return QVariant (tr ("General")); + if (le) { + return QVariant (tl::to_qstring (le->to_string (false))); + } else if (! index.parent ().isValid () && index.row () >= m_global_entries && index.row () < int (m_circuits.size ()) + m_global_entries) { + const std::pair &cp = m_circuits [index.row () - m_global_entries].first; + if (! cp.first) { + return QVariant (tr ("Circuit ") + tl::to_qstring (std::string ("-") + var_sep + cp.second->name ())); + } else if (! cp.second) { + return QVariant (tr ("Circuit ") + tl::to_qstring (cp.first->name () + var_sep + std::string ("-"))); } else if (cp.first->name () != cp.second->name ()) { return QVariant (tr ("Circuit ") + tl::to_qstring (cp.first->name () + var_sep + cp.second->name ())); } else { @@ -191,13 +234,11 @@ NetlistLogModel::data (const QModelIndex &index, int role) const } else if (role == Qt::FontRole) { - if (index.parent ().isValid ()) { - if (le && le->severity == db::NetlistCrossReference::Error) { - QFont f; - f.setBold (true); - return QVariant (f); - } - } else { + if (le) { + QFont f; + f.setBold (le->severity () == db::Error); + return QVariant (f); + } else if (! index.parent ().isValid () && index.row () >= m_global_entries && index.row () < int (m_circuits.size ()) + m_global_entries) { QFont f; f.setBold (true); return QVariant (f); @@ -205,14 +246,12 @@ NetlistLogModel::data (const QModelIndex &index, int role) const } else if (role == Qt::ForegroundRole) { - if (index.parent ().isValid ()) { - if (!le) { - // ignore - } else if (le->severity == db::NetlistCrossReference::Error) { - return QColor (255, 0, 0); - } else if (le->severity == db::NetlistCrossReference::Warning) { - return QColor (0, 0, 255); - } + if (! le) { + // ignore + } else if (le->severity () == db::Error) { + return QColor (255, 0, 0); + } else if (le->severity () == db::Warning) { + return QColor (0, 0, 255); } } diff --git a/src/layui/layui/layNetlistLogModel.h b/src/layui/layui/layNetlistLogModel.h index 751a007e0..670cf389a 100644 --- a/src/layui/layui/layNetlistLogModel.h +++ b/src/layui/layui/layNetlistLogModel.h @@ -27,8 +27,10 @@ #include "layuiCommon.h" #include "dbNetlistCrossReference.h" +#include "dbLayoutToNetlist.h" #include +#include namespace lay { @@ -40,7 +42,7 @@ class LAYUI_PUBLIC NetlistLogModel : public QAbstractItemModel { public: - NetlistLogModel (QWidget *parent, const db::NetlistCrossReference *cross_ref); + NetlistLogModel (QWidget *parent, const db::NetlistCrossReference *cross_ref, const db::LayoutToNetlist *l2n); virtual bool hasChildren (const QModelIndex &parent) const; virtual QModelIndex index (int row, int column, const QModelIndex &parent) const; @@ -50,9 +52,22 @@ public: virtual QVariant data (const QModelIndex &index, int role) const; virtual QVariant headerData (int section, Qt::Orientation orientation, int role) const; + const db::LogEntryData *log_entry (const QModelIndex &index) const; + + static QIcon icon_for_severity (db::Severity severity); + + db::Severity max_severity () const + { + return m_max_severity; + } + private: typedef std::pair, const db::NetlistCrossReference::PerCircuitData::log_entries_type *> circuit_entry; std::vector m_circuits; + const db::NetlistCrossReference::PerCircuitData::log_entries_type *mp_lvsdb_messages; + const db::LayoutToNetlist::log_entries_type *mp_l2n_messages; + int m_global_entries; + db::Severity m_max_severity; }; } diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index 7fb2b8ae4..7f000a3b6 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -275,3 +275,15 @@ TEST(29_DeviceCombineAndTolerances) run_test (_this, "res_combine2", "res_combine.gds"); run_test (_this, "res_combine3", "res_combine.gds"); } + +TEST(30_MustConnect1) +{ + run_test (_this, "must_connect1", "must_connect1.gds"); + run_test (_this, "must_connect1_tl", "must_connect1.gds"); +} + +TEST(31_MustConnect2) +{ + run_test (_this, "must_connect2", "must_connect2.gds"); +} + diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index 18c0810bf..53584222b 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -165,7 +165,7 @@ TEST(20_private) TEST(21_private) { - run_test (_this, "test_21.lylvs", "test_21.cir.gz", "test_21.gds.gz", true, "test_21_2.lvsdb"); + run_test (_this, "test_21.lylvs", "test_21.cir.gz", "test_21.gds.gz", true, "test_21_3.lvsdb"); } // issue #1021 diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index 7bea46066..ec0fcb24a 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -107,6 +107,7 @@ RUBYTEST (dbLayerMapping, "dbLayerMapping.rb") RUBYTEST (dbLibrary, "dbLibrary.rb") RUBYTEST (dbLayoutTests1, "dbLayoutTests1.rb") RUBYTEST (dbLayoutTests2, "dbLayoutTests2.rb") +RUBYTEST (dbLog, "dbLogTest.rb") RUBYTEST (dbCellTests, "dbCellTests.rb") RUBYTEST (dbRecursiveShapeIterator, "dbRecursiveShapeIterator.rb") RUBYTEST (dbRecursiveInstanceIterator, "dbRecursiveInstanceIterator.rb") diff --git a/testdata/algo/device_extract_au14_circuits.gds b/testdata/algo/device_extract_au14_circuits.gds index 7cf932fc8..11fecc306 100644 Binary files a/testdata/algo/device_extract_au14_circuits.gds and b/testdata/algo/device_extract_au14_circuits.gds differ diff --git a/testdata/algo/device_extract_au1_joined_nets.gds b/testdata/algo/device_extract_au1_joined_nets.gds index ceda8361f..919bc22a3 100644 Binary files a/testdata/algo/device_extract_au1_joined_nets.gds and b/testdata/algo/device_extract_au1_joined_nets.gds differ diff --git a/testdata/algo/device_extract_l14.gds b/testdata/algo/device_extract_l14.gds index 740a561b4..ecb5ef306 100644 Binary files a/testdata/algo/device_extract_l14.gds and b/testdata/algo/device_extract_l14.gds differ diff --git a/testdata/algo/l2n_reader_6.l2n b/testdata/algo/l2n_reader_6.l2n new file mode 100644 index 000000000..fa5e68b37 --- /dev/null +++ b/testdata/algo/l2n_reader_6.l2n @@ -0,0 +1,19 @@ +#%l2n-klayout +top(TOP) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers + +# Mask layer connectivity + +# Log entries +message(info description(info)) +message(info description(info) cell(cell_name) cat("cat name" "cat description") polygon("(1,1;2,2;3,1)")) +message(warning description(warning)) +message(error description(error)) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. diff --git a/testdata/algo/l2n_reader_6s.l2n b/testdata/algo/l2n_reader_6s.l2n new file mode 100644 index 000000000..2c9d2ed65 --- /dev/null +++ b/testdata/algo/l2n_reader_6s.l2n @@ -0,0 +1,7 @@ +#%l2n-klayout +W(TOP) +U(0.001) +H(I B(info)) +H(I B(info) C(cell_name) X("cat name" "cat description") Q("(1,1;2,2;3,1)")) +H(W B(warning)) +H(E B(error)) diff --git a/testdata/algo/l2n_reader_au_6.l2n b/testdata/algo/l2n_reader_au_6.l2n new file mode 100644 index 000000000..aac060d33 --- /dev/null +++ b/testdata/algo/l2n_reader_au_6.l2n @@ -0,0 +1,19 @@ +#%l2n-klayout +top(TOP) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers + +# Mask layer connectivity + +# Log entries +message(info description(info)) +message(info description(info) cell(cell_name) cat('cat name' 'cat description') polygon('(1,1;2,2;3,1)')) +message(warning description(warning)) +message(error description(error)) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. diff --git a/testdata/algo/l2n_writer_au_3.txt b/testdata/algo/l2n_writer_au_3.txt new file mode 100644 index 000000000..2b0c7fd7c --- /dev/null +++ b/testdata/algo/l2n_writer_au_3.txt @@ -0,0 +1,18 @@ +#%l2n-klayout +top(TOP) +unit(0.001) + +# Layer section +# This section lists the mask layers (drawing or derived) and their connections. + +# Mask layers + +# Mask layer connectivity + +# Log entries +message(info description(info)) +message(warning description(warning)) +message(error description(error)) + +# Circuit section +# Circuits are the hierarchical building blocks of the netlist. diff --git a/testdata/algo/l2n_writer_au_3s.txt b/testdata/algo/l2n_writer_au_3s.txt new file mode 100644 index 000000000..4a4b7939c --- /dev/null +++ b/testdata/algo/l2n_writer_au_3s.txt @@ -0,0 +1,6 @@ +#%l2n-klayout +W(TOP) +U(0.001) +H(I B(info)) +H(W B(warning)) +H(E B(error)) diff --git a/testdata/algo/lvs_test3.lvsdb b/testdata/algo/lvs_test3.lvsdb index d94707f4b..91f3aa00d 100644 --- a/testdata/algo/lvs_test3.lvsdb +++ b/testdata/algo/lvs_test3.lvsdb @@ -613,7 +613,17 @@ reference( # Cross reference xref( + log( + entry(error description("An error (global)")) + entry(warning description("A warning (global)")) + entry(info description("Some info (global)")) + ) circuit(() INV2PAIRX mismatch + log( + entry(error description("An error")) + entry(warning description("A warning")) + entry(info description("Some info")) + ) kk( jj("hello" W) jj("world!" E) ) # invalid xref( ) @@ -684,4 +694,5 @@ xref( circuit(5 5 match) ) ) + kk( xx(1 2 3) ) # invalid ) diff --git a/testdata/algo/lvs_test3_au.lvsdb b/testdata/algo/lvs_test3_au.lvsdb index fd107f1b9..617d84271 100644 --- a/testdata/algo/lvs_test3_au.lvsdb +++ b/testdata/algo/lvs_test3_au.lvsdb @@ -602,7 +602,17 @@ reference( # Cross reference xref( + log( + entry(error description('An error (global)')) + entry(warning description('A warning (global)')) + entry(info description('Some info (global)')) + ) circuit(() INV2PAIRX mismatch + log( + entry(error description('An error')) + entry(warning description('A warning')) + entry(info description('Some info')) + ) xref( ) ) diff --git a/testdata/drc/drcSimpleTests_12.drc b/testdata/drc/drcSimpleTests_12.drc index ed7b9c996..0c21bad68 100644 --- a/testdata/drc/drcSimpleTests_12.drc +++ b/testdata/drc/drcSimpleTests_12.drc @@ -21,8 +21,8 @@ metal2_lbl = input(8, 1) # Computed layers pactive = active & nwell -pgate = active & poly -psd = active - pgate +pgate = pactive & poly +psd = pactive - pgate nactive = active - nwell ngate = nactive & poly diff --git a/testdata/drc/drcSimpleTests_au12a.cir b/testdata/drc/drcSimpleTests_au12a.cir index b459b2a77..8b0577698 100644 --- a/testdata/drc/drcSimpleTests_au12a.cir +++ b/testdata/drc/drcSimpleTests_au12a.cir @@ -5,12 +5,12 @@ * net 1 FB * net 2 OSC * net 3 NEXT -* net 4 VSSZ,VSS -* net 5 VDDZ,VDD -* cell instance $1 r180 *1 -0.24,9.18 -X$1 16 1 2 4 5 INV2 -* cell instance $2 r0 *1 0,0 -X$2 1 14 15 4 5 INV2 +* net 4 VSSZ +* net 5 VDDZ +* cell instance $1 r0 *1 0,0 +X$1 1 14 15 4 5 INV2 +* cell instance $2 r180 *1 -0.24,9.18 +X$2 16 1 2 4 5 INV2 * cell instance $3 r180 *1 10.32,9.18 X$3 3 9 19 4 5 INV2 * cell instance $4 r0 *1 10.56,0 @@ -46,18 +46,14 @@ X$2 2 5 1 TRANS X$3 5 3 2 TRANS * cell instance $4 m0 *1 0.4,0 X$4 4 3 2 TRANS -* device instance $1 -0.4,0 PMOS -M$1 2 1 4 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U -* device instance $2 0.4,0 PMOS -M$2 4 2 3 4 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U -* device instance $3 -0.4,2.8 PMOS -M$3 2 1 5 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U -* device instance $4 0.4,2.8 PMOS -M$4 5 2 3 5 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U -* device instance $5 -0.4,0 NMOS -M$5 2 1 4 2 NMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U -* device instance $6 0.4,0 NMOS -M$6 4 2 3 4 NMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +* device instance $1 r0 *1 -0.4,2.8 PMOS +M$1 5 1 2 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $2 r0 *1 0.4,2.8 PMOS +M$2 3 2 5 5 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +* device instance $3 r0 *1 -0.4,0 NMOS +M$3 4 1 2 2 NMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $4 r0 *1 0.4,0 NMOS +M$4 3 2 4 4 NMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U .ENDS INV2 * cell TRANS diff --git a/testdata/drc/implicit_nets.gds b/testdata/drc/implicit_nets.gds index 0b8e3faf9..1ea4c7aaf 100644 Binary files a/testdata/drc/implicit_nets.gds and b/testdata/drc/implicit_nets.gds differ diff --git a/testdata/lvs/bbdevices3b.lvsdb b/testdata/lvs/bbdevices3b.lvsdb index 95ff6ee8b..d4573f7a4 100644 --- a/testdata/lvs/bbdevices3b.lvsdb +++ b/testdata/lvs/bbdevices3b.lvsdb @@ -280,6 +280,9 @@ reference( # Cross reference xref( + log( + entry(error description('Circuits testall and TESTALL could not be compared because the following subcircuits failed to compare:\n B: BWBTEST')) + ) circuit(BBGATEST BBGATEST match xref( pin(() 1 match) diff --git a/testdata/lvs/bbdevices4b.lvsdb b/testdata/lvs/bbdevices4b.lvsdb index b91a05e0c..90993b7b2 100644 --- a/testdata/lvs/bbdevices4b.lvsdb +++ b/testdata/lvs/bbdevices4b.lvsdb @@ -275,6 +275,9 @@ reference( # Cross reference xref( + log( + entry(error description('Circuits testall and TESTALL could not be compared because the following subcircuits failed to compare:\n B: BWBTEST')) + ) circuit(BBGATEST BBGATEST match xref( pin(() 1 match) diff --git a/testdata/lvs/double_height2.lvs b/testdata/lvs/double_height2.lvs index cac92ec75..71e32a05c 100644 --- a/testdata/lvs/double_height2.lvs +++ b/testdata/lvs/double_height2.lvs @@ -3,6 +3,8 @@ source($lvs_test_source) report_lvs($lvs_test_target_lvsdb) +ignore_extraction_errors(true) + writer = write_spice(true, false) target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") diff --git a/testdata/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb index e794e299c..cb79afe2b 100644 --- a/testdata/lvs/double_height2.lvsdb +++ b/testdata/lvs/double_height2.lvsdb @@ -27,6 +27,9 @@ J( C(l2 l6 l2) C(l5 l6 l5) G(l14 SUBSTRATE) + H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) + H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) + H(E B('Must-connect nets R of circuit INV2 are not connected') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/double_height2_texts.lvs b/testdata/lvs/double_height2_texts.lvs index 33ae20e7f..c3d169a9d 100644 --- a/testdata/lvs/double_height2_texts.lvs +++ b/testdata/lvs/double_height2_texts.lvs @@ -3,6 +3,8 @@ source($lvs_test_source) report_lvs($lvs_test_target_lvsdb) +ignore_extraction_errors(true) + writer = write_spice(true, false) target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") diff --git a/testdata/lvs/double_height2_texts.lvsdb b/testdata/lvs/double_height2_texts.lvsdb index 2c986d071..509b5b815 100644 --- a/testdata/lvs/double_height2_texts.lvsdb +++ b/testdata/lvs/double_height2_texts.lvsdb @@ -27,6 +27,9 @@ J( C(l2 l6 l2) C(l5 l6 l5) G(l14 SUBSTRATE) + H(W B('Must-connect nets GND must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) + H(W B('Must-connect nets R must be connected further up in the hierarchy - this is an error at chip top level') C(INVCHAIN) X('must-connect')) + H(E B('Must-connect nets R of circuit INV2 are not connected') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) K(PMOS MOS3) K(NMOS MOS3) D(D$PMOS PMOS diff --git a/testdata/lvs/must_connect1.cir b/testdata/lvs/must_connect1.cir new file mode 100644 index 000000000..9b6488f75 --- /dev/null +++ b/testdata/lvs/must_connect1.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +.SUBCKT TOP VSSTOP A Q +X$1 \$8 \$2 \$1 \$1 Q VSSTOP INV2 +X$2 A \$3 VSSTOP \$3 \$2 \$8 INVCHAIN +.ENDS TOP + +.SUBCKT INVCHAIN IN IN2 VSS|VSS2|VSS2B OUT OUT2 VDD +X$1 VDD IN2 \$1 \$1 OUT2 VSS|VSS2|VSS2B INV2 +X$2 VDD IN \$2 \$2 OUT VSS|VSS2|VSS2B INV2 +.ENDS INVCHAIN + +.SUBCKT INV2 VDD A1 A2 Q1 Q2 VSS +X$1 VSS VDD A2 Q2 INV +X$2 VSS VDD A1 Q1 INV +.ENDS INV2 + +.SUBCKT INV \$1 \$2 \$3 \$4 +M$1 \$2 \$3 \$4 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$1 \$3 \$4 \$4 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/must_connect1.gds b/testdata/lvs/must_connect1.gds new file mode 100644 index 000000000..cf15929c5 Binary files /dev/null and b/testdata/lvs/must_connect1.gds differ diff --git a/testdata/lvs/must_connect1.lvs b/testdata/lvs/must_connect1.lvs new file mode 100644 index 000000000..799fa0364 --- /dev/null +++ b/testdata/lvs/must_connect1.lvs @@ -0,0 +1,139 @@ + +$lvs_test_source && source($lvs_test_source) + +if $lvs_test_target_lvsdb + report_lvs($lvs_test_target_lvsdb) +else + report_lvs +end + + +writer = write_spice(true, false) +$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +# needs this delegate because we use MOS3 which is not available in Spice +class SpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate + + # says we want to catch these subcircuits as devices + def wants_subcircuit(name) + name == "HVNMOS" || name == "HVPMOS" + end + + # translate the element + def element(circuit, el, name, model, value, nets, params) + + if el != "M" + # all other elements are left to the standard implementation + return super + end + + if nets.size != 4 + error("Device #{model} needs four nodes") + end + + # provide a device class + cls = circuit.netlist.device_class_by_name(model) + if ! cls + cls = RBA::DeviceClassMOS3Transistor::new + cls.name = model + circuit.netlist.add(cls) + end + + # create a device + device = circuit.create_device(cls, name) + + # and configure the device + [ "S", "G", "D" ].each_with_index do |t,index| + device.connect_terminal(t, nets[index]) + end + device.set_parameter("W", params["W"] * 1e6) + device.set_parameter("L", params["L"] * 1e6) + + device + + end + +end + +reader = RBA::NetlistSpiceReader::new(SpiceReaderDelegate::new) +schematic(File.basename(source.path, ".*") + ".sch", reader) + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +poly = input(3, 0) +poly_lbl = input(3, 1) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +metal1_lbl = input(6, 1) +via1 = input(7, 0) +metal2 = input(8, 0) +metal2_lbl = input(8, 1) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +psd = nil +nsd = nil + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos3("PMOS"), { "SD" => psd, "G" => pgate, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos3("NMOS"), { "SD" => nsd, "G" => ngate, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Global +connect_global(bulk, "SUBSTRATE") + +# Implicit connection of the INV2 +# VSS nets +connect_implicit("INV2", "VSS") +connect_implicit("TOP", "VSS*") +# Fix 1 +connect_explicit("INVCHAIN", ["VSS2", "VSS2B", "VSS"]) +connect_implicit("INVCHAIN", "VDD") + +# Compare section + +netlist.simplify +align + +compare + diff --git a/testdata/lvs/must_connect1.lvsdb b/testdata/lvs/must_connect1.lvsdb new file mode 100644 index 000000000..6d86052d3 --- /dev/null +++ b/testdata/lvs/must_connect1.lvsdb @@ -0,0 +1,484 @@ +#%lvsdb-klayout +J( + W(TOP) + U(0.001) + L(l3 '3/0') + L(l11 '3/1') + L(l6 '4/0') + L(l7 '5/0') + L(l8 '6/0') + L(l12 '6/1') + L(l9 '7/0') + L(l10 '8/0') + L(l13 '8/1') + L(l14) + L(l2) + L(l5) + C(l3 l3 l11 l7) + C(l11 l3 l11) + C(l6 l6 l8 l2 l5) + C(l7 l3 l7 l8) + C(l8 l6 l7 l8 l12 l9) + C(l12 l8 l12) + C(l9 l8 l9 l10) + C(l10 l9 l10 l13) + C(l13 l10 l13) + C(l14 l14) + C(l2 l6 l2) + C(l5 l6 l5) + G(l14 SUBSTRATE) + H(W B('Must-connect nets VSSTOP must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) + K(PMOS MOS3) + K(NMOS MOS3) + D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + ) + D(D$NMOS NMOS + T(S + R(l5 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l5 (125 -475) (775 950)) + ) + ) + X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l6 (290 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l5 (-1375 -925) (775 950)) + ) + N(2 + R(l6 (290 2490) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-125 -250) (250 2500)) + R(l3 (-250 -3050) (250 1600)) + R(l3 (-250 1200) (250 1600)) + ) + N(4 + R(l6 (-510 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l6 (-220 2180) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -3530) (360 2840)) + R(l8 (-360 -2800) (360 760)) + R(l8 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l5 (-775 -3750) (775 950)) + ) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + ) + ) + X(INV2 + R((0 0) (3000 9200)) + N(1 I(VDD) + R(l10 (0 3150) (3000 2900)) + R(l13 (-1890 -1450) (0 0)) + ) + N(2 I(A1) + R(l11 (1480 7110) (0 0)) + ) + N(3 I(A2) + R(l11 (1520 1950) (0 0)) + ) + N(4 I(Q1) + R(l12 (1920 7070) (0 0)) + ) + N(5 I(Q2) + R(l12 (1940 1950) (0 0)) + ) + N(6 I(VSS) + R(l13 (2680 8390) (0 0)) + R(l13 (-30 -7640) (0 0)) + ) + P(1 I(VDD)) + P(2 I(A1)) + P(3 I(A2)) + P(4 I(Q1)) + P(5 I(Q2)) + P(6 I(VSS)) + X(1 INV M O(180) Y(1500 800) + P(0 6) + P(1 1) + P(2 3) + P(3 5) + ) + X(2 INV O(180) Y(1500 8400) + P(0 6) + P(1 1) + P(2 2) + P(3 4) + ) + ) + X(INVCHAIN + R((-915 -15) (10415 9215)) + N(1 + R(l3 (7340 1650) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (1625 1835) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(3 I(IN) + R(l3 (-90 6850) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(4 I(IN2) + R(l3 (5665 6790) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(5 I('VSS,VSS2,VSS2B') + R(l10 (-915 675) (915 250)) + R(l10 (-915 0) (250 7325)) + R(l10 (-250 0) (915 250)) + R(l13 (-510 -125) (0 0)) + R(l13 (8990 -255) (0 0)) + R(l13 (25 -7115) (0 0)) + ) + N(6 I(OUT) + R(l12 (1890 2105) (0 0)) + ) + N(7 I(OUT2) + R(l12 (7730 2155) (0 0)) + ) + N(8 I(VDD) + R(l13 (8035 4540) (0 0)) + R(l13 (-5735 60) (0 0)) + ) + P(3 I(IN)) + P(4 I(IN2)) + P(5 I('VSS,VSS2,VSS2B')) + P(6 I(OUT)) + P(7 I(OUT2)) + P(8 I(VDD)) + X(1 INV2 Y(5780 -15) + P(0 8) + P(1 4) + P(2 1) + P(3 1) + P(4 7) + P(5 5) + ) + X(2 INV2 Y(0 0) + P(0 8) + P(1 3) + P(2 2) + P(3 2) + P(4 6) + P(5 5) + ) + ) + X(TOP + R((-305 350) (15415 9225)) + N(1 + R(l3 (12950 2130) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (12100 7300) (640 530)) + R(l7 (-540 -415) (270 250)) + R(l8 (-1695 -250) (1695 250)) + R(l8 (-4075 -5650) (2630 250)) + R(l8 (-250 0) (250 5150)) + ) + N(3 + R(l7 (6465 7325) (220 240)) + R(l8 (-4100 -5365) (3125 250)) + R(l8 (-250 0) (250 4860)) + R(l8 (-250 0) (1225 250)) + ) + N(4 I(VSSTOP) + R(l10 (3610 8300) (2815 440)) + R(l10 (-710 -250) (0 0)) + R(l10 (3675 -165) (1975 565)) + R(l10 (-1975 -8190) (1975 575)) + R(l10 (-1005 -255) (0 0)) + ) + N(5 I(A) + R(l11 (975 7530) (0 0)) + ) + N(6 I(Q) + R(l12 (13260 2010) (0 0)) + ) + N(7 + R(l10 (3450 4840) (3055 250)) + R(l10 (2885 -250) (1975 250)) + ) + P(4 I(VSSTOP)) + P(5 I(A)) + P(6 I(Q)) + X(1 INV2 Y(11365 375) + P(0 7) + P(1 2) + P(2 1) + P(3 1) + P(4 6) + P(5 4) + ) + X(2 INVCHAIN Y(610 365) + P(0 5) + P(1 3) + P(2 4) + P(3 3) + P(4 2) + P(5 7) + ) + ) +) +H( + K(PMOS MOS3) + K(NMOS MOS3) + X(INV + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I(Q)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A)) + P(4 I(Q)) + D(1 PMOS + I($1) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 1) + T(G 3) + T(D 4) + ) + D(2 NMOS + I($3) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 2) + T(G 3) + T(D 4) + ) + ) + X(INV2 + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + ) + X(2 INV I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + ) + ) + X(INVCHAIN + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + N(7 I('1')) + N(8 I('2')) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV2 I($2) + P(0 1) + P(1 2) + P(2 3) + P(3 7) + P(4 7) + P(5 4) + ) + X(2 INV2 I($3) + P(0 1) + P(1 2) + P(2 5) + P(3 8) + P(4 8) + P(5 6) + ) + ) + X(TOP + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I('1')) + N(5 I('3')) + N(6 I('2')) + N(7 I(Q)) + X(1 INVCHAIN I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + P(4 4) + P(5 5) + ) + X(2 INV2 I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + P(4 6) + P(5 7) + ) + ) +) +Z( + X(INV INV 1 + Z( + N(3 3 1) + N(4 4 1) + N(2 1 1) + N(1 2 1) + P(2 2 1) + P(3 3 1) + P(1 0 1) + P(0 1 1) + D(2 2 1) + D(1 1 1) + ) + ) + X(INV2 INV2 1 + Z( + N(2 3 1) + N(3 5 1) + N(4 4 1) + N(5 6 1) + N(1 1 1) + N(6 2 1) + P(1 2 1) + P(2 4 1) + P(3 3 1) + P(4 5 1) + P(0 0 1) + P(5 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(INVCHAIN INVCHAIN 1 + L( + M(W B('Matching nets OUT vs. Q1 from an ambiguous group of nets')) + M(W B('Matching nets OUT2 vs. Q2 from an ambiguous group of nets')) + M(I B('Matching nets $2 vs. 1 following an ambiguous match')) + M(I B('Matching nets IN vs. A1 following an ambiguous match')) + M(I B('Matching nets $1 vs. 2 following an ambiguous match')) + M(I B('Matching nets IN2 vs. A2 following an ambiguous match')) + ) + Z( + N(2 7 1) + N(1 8 1) + N(3 3 1) + N(4 5 1) + N(6 4 W) + N(7 6 W) + N(8 1 1) + N(5 2 1) + P(0 2 1) + P(1 4 1) + P(3 3 1) + P(4 5 1) + P(5 0 1) + P(2 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(TOP TOP 1 + Z( + N(3 4 1) + N(1 6 1) + N(2 5 1) + N(7 1 1) + N(5 3 1) + N(6 7 1) + N(4 2 1) + P(1 () 1) + P(2 () 1) + P(0 () 1) + X(1 2 1) + X(2 1 1) + ) + ) +) diff --git a/testdata/lvs/must_connect1.sch b/testdata/lvs/must_connect1.sch new file mode 100644 index 000000000..9114c6de9 --- /dev/null +++ b/testdata/lvs/must_connect1.sch @@ -0,0 +1,24 @@ + +.SUBCKT TOP +X$1 VDD VSS A 1 1 3 INVCHAIN +X$2 VDD VSS 3 2 2 Q INV2 +.ENDS TOP + +* cell INVCHAIN +.SUBCKT INVCHAIN VDD VSS A1 Q1 A2 Q2 +X$2 VDD VSS A1 1 1 Q1 INV2 +X$3 VDD VSS A2 2 2 Q2 INV2 +.ENDS INVCHAIN + +* cell INV2 +.SUBCKT INV2 VDD VSS A1 Q1 A2 Q2 +X$1 VDD VSS A1 Q1 INV +X$2 VDD VSS A2 Q2 INV +.ENDS INV2 + +* cell INV +.SUBCKT INV VDD VSS A Q +M$1 VDD A Q VDD PMOS L=0.25U W=0.95U +M$3 VSS A Q VSS NMOS L=0.25U W=0.95U +.ENDS INV + diff --git a/testdata/lvs/must_connect1_tl.cir b/testdata/lvs/must_connect1_tl.cir new file mode 100644 index 000000000..9b6488f75 --- /dev/null +++ b/testdata/lvs/must_connect1_tl.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +.SUBCKT TOP VSSTOP A Q +X$1 \$8 \$2 \$1 \$1 Q VSSTOP INV2 +X$2 A \$3 VSSTOP \$3 \$2 \$8 INVCHAIN +.ENDS TOP + +.SUBCKT INVCHAIN IN IN2 VSS|VSS2|VSS2B OUT OUT2 VDD +X$1 VDD IN2 \$1 \$1 OUT2 VSS|VSS2|VSS2B INV2 +X$2 VDD IN \$2 \$2 OUT VSS|VSS2|VSS2B INV2 +.ENDS INVCHAIN + +.SUBCKT INV2 VDD A1 A2 Q1 Q2 VSS +X$1 VSS VDD A2 Q2 INV +X$2 VSS VDD A1 Q1 INV +.ENDS INV2 + +.SUBCKT INV \$1 \$2 \$3 \$4 +M$1 \$2 \$3 \$4 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$1 \$3 \$4 \$4 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/must_connect1_tl.lvs b/testdata/lvs/must_connect1_tl.lvs new file mode 100644 index 000000000..4e9cc35ed --- /dev/null +++ b/testdata/lvs/must_connect1_tl.lvs @@ -0,0 +1,143 @@ + +$lvs_test_source && source($lvs_test_source) + +if $lvs_test_target_lvsdb + report_lvs($lvs_test_target_lvsdb) +else + report_lvs +end + + +writer = write_spice(true, false) +$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +# Turns the warning about VSSTOP into an error +top_level(true) +ignore_extraction_errors(true) + +# needs this delegate because we use MOS3 which is not available in Spice +class SpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate + + # says we want to catch these subcircuits as devices + def wants_subcircuit(name) + name == "HVNMOS" || name == "HVPMOS" + end + + # translate the element + def element(circuit, el, name, model, value, nets, params) + + if el != "M" + # all other elements are left to the standard implementation + return super + end + + if nets.size != 4 + error("Device #{model} needs four nodes") + end + + # provide a device class + cls = circuit.netlist.device_class_by_name(model) + if ! cls + cls = RBA::DeviceClassMOS3Transistor::new + cls.name = model + circuit.netlist.add(cls) + end + + # create a device + device = circuit.create_device(cls, name) + + # and configure the device + [ "S", "G", "D" ].each_with_index do |t,index| + device.connect_terminal(t, nets[index]) + end + device.set_parameter("W", params["W"] * 1e6) + device.set_parameter("L", params["L"] * 1e6) + + device + + end + +end + +reader = RBA::NetlistSpiceReader::new(SpiceReaderDelegate::new) +schematic(File.basename(source.path, ".*") + ".sch", reader) + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +poly = input(3, 0) +poly_lbl = input(3, 1) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +metal1_lbl = input(6, 1) +via1 = input(7, 0) +metal2 = input(8, 0) +metal2_lbl = input(8, 1) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +psd = nil +nsd = nil + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos3("PMOS"), { "SD" => psd, "G" => pgate, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos3("NMOS"), { "SD" => nsd, "G" => ngate, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Global +connect_global(bulk, "SUBSTRATE") + +# Implicit connection of the INV2 +# VSS nets +connect_implicit("INV2", "VSS") +connect_implicit("TOP", "VSS*") +# Fix 1 +connect_explicit("INVCHAIN", ["VSS2", "VSS2B", "VSS"]) +connect_implicit("INVCHAIN", "VDD") + +# Compare section + +netlist.simplify +align + +compare + diff --git a/testdata/lvs/must_connect1_tl.lvsdb b/testdata/lvs/must_connect1_tl.lvsdb new file mode 100644 index 000000000..0bd8468c4 --- /dev/null +++ b/testdata/lvs/must_connect1_tl.lvsdb @@ -0,0 +1,484 @@ +#%lvsdb-klayout +J( + W(TOP) + U(0.001) + L(l3 '3/0') + L(l11 '3/1') + L(l6 '4/0') + L(l7 '5/0') + L(l8 '6/0') + L(l12 '6/1') + L(l9 '7/0') + L(l10 '8/0') + L(l13 '8/1') + L(l14) + L(l2) + L(l5) + C(l3 l3 l11 l7) + C(l11 l3 l11) + C(l6 l6 l8 l2 l5) + C(l7 l3 l7 l8) + C(l8 l6 l7 l8 l12 l9) + C(l12 l8 l12) + C(l9 l8 l9 l10) + C(l10 l9 l10 l13) + C(l13 l10 l13) + C(l14 l14) + C(l2 l6 l2) + C(l5 l6 l5) + G(l14 SUBSTRATE) + H(E B('Must-connect nets VSSTOP must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) + K(PMOS MOS3) + K(NMOS MOS3) + D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + ) + D(D$NMOS NMOS + T(S + R(l5 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l5 (125 -475) (775 950)) + ) + ) + X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l6 (290 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l5 (-1375 -925) (775 950)) + ) + N(2 + R(l6 (290 2490) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-125 -250) (250 2500)) + R(l3 (-250 -3050) (250 1600)) + R(l3 (-250 1200) (250 1600)) + ) + N(4 + R(l6 (-510 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l6 (-220 2180) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -3530) (360 2840)) + R(l8 (-360 -2800) (360 760)) + R(l8 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l5 (-775 -3750) (775 950)) + ) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + ) + ) + X(INV2 + R((0 0) (3000 9200)) + N(1 I(VDD) + R(l10 (0 3150) (3000 2900)) + R(l13 (-1890 -1450) (0 0)) + ) + N(2 I(A1) + R(l11 (1480 7110) (0 0)) + ) + N(3 I(A2) + R(l11 (1520 1950) (0 0)) + ) + N(4 I(Q1) + R(l12 (1920 7070) (0 0)) + ) + N(5 I(Q2) + R(l12 (1940 1950) (0 0)) + ) + N(6 I(VSS) + R(l13 (2680 8390) (0 0)) + R(l13 (-30 -7640) (0 0)) + ) + P(1 I(VDD)) + P(2 I(A1)) + P(3 I(A2)) + P(4 I(Q1)) + P(5 I(Q2)) + P(6 I(VSS)) + X(1 INV M O(180) Y(1500 800) + P(0 6) + P(1 1) + P(2 3) + P(3 5) + ) + X(2 INV O(180) Y(1500 8400) + P(0 6) + P(1 1) + P(2 2) + P(3 4) + ) + ) + X(INVCHAIN + R((-915 -15) (10415 9215)) + N(1 + R(l3 (7340 1650) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (1625 1835) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(3 I(IN) + R(l3 (-90 6850) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(4 I(IN2) + R(l3 (5665 6790) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(5 I('VSS,VSS2,VSS2B') + R(l10 (-915 675) (915 250)) + R(l10 (-915 0) (250 7325)) + R(l10 (-250 0) (915 250)) + R(l13 (-510 -125) (0 0)) + R(l13 (8990 -255) (0 0)) + R(l13 (25 -7115) (0 0)) + ) + N(6 I(OUT) + R(l12 (1890 2105) (0 0)) + ) + N(7 I(OUT2) + R(l12 (7730 2155) (0 0)) + ) + N(8 I(VDD) + R(l13 (8035 4540) (0 0)) + R(l13 (-5735 60) (0 0)) + ) + P(3 I(IN)) + P(4 I(IN2)) + P(5 I('VSS,VSS2,VSS2B')) + P(6 I(OUT)) + P(7 I(OUT2)) + P(8 I(VDD)) + X(1 INV2 Y(5780 -15) + P(0 8) + P(1 4) + P(2 1) + P(3 1) + P(4 7) + P(5 5) + ) + X(2 INV2 Y(0 0) + P(0 8) + P(1 3) + P(2 2) + P(3 2) + P(4 6) + P(5 5) + ) + ) + X(TOP + R((-305 350) (15415 9225)) + N(1 + R(l3 (12950 2130) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (12100 7300) (640 530)) + R(l7 (-540 -415) (270 250)) + R(l8 (-1695 -250) (1695 250)) + R(l8 (-4075 -5650) (2630 250)) + R(l8 (-250 0) (250 5150)) + ) + N(3 + R(l7 (6465 7325) (220 240)) + R(l8 (-4100 -5365) (3125 250)) + R(l8 (-250 0) (250 4860)) + R(l8 (-250 0) (1225 250)) + ) + N(4 I(VSSTOP) + R(l10 (3610 8300) (2815 440)) + R(l10 (-710 -250) (0 0)) + R(l10 (3675 -165) (1975 565)) + R(l10 (-1975 -8190) (1975 575)) + R(l10 (-1005 -255) (0 0)) + ) + N(5 I(A) + R(l11 (975 7530) (0 0)) + ) + N(6 I(Q) + R(l12 (13260 2010) (0 0)) + ) + N(7 + R(l10 (3450 4840) (3055 250)) + R(l10 (2885 -250) (1975 250)) + ) + P(4 I(VSSTOP)) + P(5 I(A)) + P(6 I(Q)) + X(1 INV2 Y(11365 375) + P(0 7) + P(1 2) + P(2 1) + P(3 1) + P(4 6) + P(5 4) + ) + X(2 INVCHAIN Y(610 365) + P(0 5) + P(1 3) + P(2 4) + P(3 3) + P(4 2) + P(5 7) + ) + ) +) +H( + K(PMOS MOS3) + K(NMOS MOS3) + X(INV + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I(Q)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A)) + P(4 I(Q)) + D(1 PMOS + I($1) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 1) + T(G 3) + T(D 4) + ) + D(2 NMOS + I($3) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 2) + T(G 3) + T(D 4) + ) + ) + X(INV2 + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + ) + X(2 INV I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + ) + ) + X(INVCHAIN + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + N(7 I('1')) + N(8 I('2')) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV2 I($2) + P(0 1) + P(1 2) + P(2 3) + P(3 7) + P(4 7) + P(5 4) + ) + X(2 INV2 I($3) + P(0 1) + P(1 2) + P(2 5) + P(3 8) + P(4 8) + P(5 6) + ) + ) + X(TOP + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I('1')) + N(5 I('3')) + N(6 I('2')) + N(7 I(Q)) + X(1 INVCHAIN I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + P(4 4) + P(5 5) + ) + X(2 INV2 I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + P(4 6) + P(5 7) + ) + ) +) +Z( + X(INV INV 1 + Z( + N(3 3 1) + N(4 4 1) + N(2 1 1) + N(1 2 1) + P(2 2 1) + P(3 3 1) + P(1 0 1) + P(0 1 1) + D(2 2 1) + D(1 1 1) + ) + ) + X(INV2 INV2 1 + Z( + N(2 3 1) + N(3 5 1) + N(4 4 1) + N(5 6 1) + N(1 1 1) + N(6 2 1) + P(1 2 1) + P(2 4 1) + P(3 3 1) + P(4 5 1) + P(0 0 1) + P(5 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(INVCHAIN INVCHAIN 1 + L( + M(W B('Matching nets OUT vs. Q1 from an ambiguous group of nets')) + M(W B('Matching nets OUT2 vs. Q2 from an ambiguous group of nets')) + M(I B('Matching nets $2 vs. 1 following an ambiguous match')) + M(I B('Matching nets IN vs. A1 following an ambiguous match')) + M(I B('Matching nets $1 vs. 2 following an ambiguous match')) + M(I B('Matching nets IN2 vs. A2 following an ambiguous match')) + ) + Z( + N(2 7 1) + N(1 8 1) + N(3 3 1) + N(4 5 1) + N(6 4 W) + N(7 6 W) + N(8 1 1) + N(5 2 1) + P(0 2 1) + P(1 4 1) + P(3 3 1) + P(4 5 1) + P(5 0 1) + P(2 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(TOP TOP 1 + Z( + N(3 4 1) + N(1 6 1) + N(2 5 1) + N(7 1 1) + N(5 3 1) + N(6 7 1) + N(4 2 1) + P(1 () 1) + P(2 () 1) + P(0 () 1) + X(1 2 1) + X(2 1 1) + ) + ) +) diff --git a/testdata/lvs/must_connect2.cir b/testdata/lvs/must_connect2.cir new file mode 100644 index 000000000..fc641f467 --- /dev/null +++ b/testdata/lvs/must_connect2.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +.SUBCKT TOP VSSTOP A Q VDD +X$1 VDD \$2 \$1 \$1 Q VSSTOP INV2 +X$2 A \$3 VSSTOP \$3 \$2 VDD INVCHAIN +.ENDS TOP + +.SUBCKT INVCHAIN IN IN2 VSS|VSS2|VSS2B OUT OUT2 VDD +X$1 VDD IN2 \$1 \$1 OUT2 VSS|VSS2|VSS2B INV2 +X$2 VDD IN \$2 \$2 OUT VSS|VSS2|VSS2B INV2 +.ENDS INVCHAIN + +.SUBCKT INV2 VDD A1 A2 Q1 Q2 VSS +X$1 VSS VDD A2 Q2 INV +X$2 VSS VDD A1 Q1 INV +.ENDS INV2 + +.SUBCKT INV \$1 \$2 \$3 \$4 +M$1 \$2 \$3 \$4 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$1 \$3 \$4 \$4 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/must_connect2.gds b/testdata/lvs/must_connect2.gds new file mode 100644 index 000000000..885df5c13 Binary files /dev/null and b/testdata/lvs/must_connect2.gds differ diff --git a/testdata/lvs/must_connect2.lvs b/testdata/lvs/must_connect2.lvs new file mode 100644 index 000000000..ac34742e7 --- /dev/null +++ b/testdata/lvs/must_connect2.lvs @@ -0,0 +1,142 @@ + +$lvs_test_source && source($lvs_test_source) + +if $lvs_test_target_lvsdb + report_lvs($lvs_test_target_lvsdb) +else + report_lvs +end + +# Implicit connection of the INV2 +# VSS nets +connect_implicit("INV2", "VSS") +connect_implicit("TOP", "VSS*") +# Fix 1 +connect_explicit("INVCHAIN", ["VSS2", "VSS2B", "VSS"]) +connect_implicit("INVCHAIN", "VDD") +connect_explicit("TOP", ["VDD"]) + +ignore_extraction_errors(true) + +writer = write_spice(true, false) +$lvs_test_target_cir && target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +# needs this delegate because we use MOS3 which is not available in Spice +class SpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate + + # says we want to catch these subcircuits as devices + def wants_subcircuit(name) + name == "HVNMOS" || name == "HVPMOS" + end + + # translate the element + def element(circuit, el, name, model, value, nets, params) + + if el != "M" + # all other elements are left to the standard implementation + return super + end + + if nets.size != 4 + error("Device #{model} needs four nodes") + end + + # provide a device class + cls = circuit.netlist.device_class_by_name(model) + if ! cls + cls = RBA::DeviceClassMOS3Transistor::new + cls.name = model + circuit.netlist.add(cls) + end + + # create a device + device = circuit.create_device(cls, name) + + # and configure the device + [ "S", "G", "D" ].each_with_index do |t,index| + device.connect_terminal(t, nets[index]) + end + device.set_parameter("W", params["W"] * 1e6) + device.set_parameter("L", params["L"] * 1e6) + + device + + end + +end + +reader = RBA::NetlistSpiceReader::new(SpiceReaderDelegate::new) +schematic(File.basename(source.path, ".*") + ".sch", reader) + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +poly = input(3, 0) +poly_lbl = input(3, 1) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +metal1_lbl = input(6, 1) +via1 = input(7, 0) +metal2 = input(8, 0) +metal2_lbl = input(8, 1) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +psd = nil +nsd = nil + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos3("PMOS"), { "SD" => psd, "G" => pgate, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos3("NMOS"), { "SD" => nsd, "G" => ngate, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Global +connect_global(bulk, "SUBSTRATE") + +# Compare section + +netlist.simplify +align + +# Skip as we have errors .. +compare + diff --git a/testdata/lvs/must_connect2.lvsdb b/testdata/lvs/must_connect2.lvsdb new file mode 100644 index 000000000..b3fd8d3a3 --- /dev/null +++ b/testdata/lvs/must_connect2.lvsdb @@ -0,0 +1,490 @@ +#%lvsdb-klayout +J( + W(TOP) + U(0.001) + L(l3 '3/0') + L(l11 '3/1') + L(l6 '4/0') + L(l7 '5/0') + L(l8 '6/0') + L(l12 '6/1') + L(l9 '7/0') + L(l10 '8/0') + L(l13 '8/1') + L(l14) + L(l2) + L(l5) + C(l3 l3 l11 l7) + C(l11 l3 l11) + C(l6 l6 l8 l2 l5) + C(l7 l3 l7 l8) + C(l8 l6 l7 l8 l12 l9) + C(l12 l8 l12) + C(l9 l8 l9 l10) + C(l10 l9 l10 l13) + C(l13 l10 l13) + C(l14 l14) + C(l2 l6 l2) + C(l5 l6 l5) + G(l14 SUBSTRATE) + H(W B('Must-connect nets VSSTOP must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) + H(W B('Must-connect nets VDD must be connected further up in the hierarchy - this is an error at chip top level') C(TOP) X('must-connect')) + H(E B('Must-connect nets VSS of circuit INV2 are not connected') C(INVCHAIN) X('must-connect') Q('(0,0;0,9.2;3,9.2;3,0)')) + K(PMOS MOS3) + K(NMOS MOS3) + D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + ) + D(D$NMOS NMOS + T(S + R(l5 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l5 (125 -475) (775 950)) + ) + ) + X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l6 (290 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l5 (-1375 -925) (775 950)) + ) + N(2 + R(l6 (290 2490) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-125 -250) (250 2500)) + R(l3 (-250 -3050) (250 1600)) + R(l3 (-250 1200) (250 1600)) + ) + N(4 + R(l6 (-510 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l6 (-220 2180) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -3530) (360 2840)) + R(l8 (-360 -2800) (360 760)) + R(l8 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l5 (-775 -3750) (775 950)) + ) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + ) + ) + X(INV2 + R((0 0) (3000 9200)) + N(1 I(VDD) + R(l10 (0 3150) (3000 2900)) + R(l13 (-1890 -1450) (0 0)) + ) + N(2 I(A1) + R(l11 (1480 7110) (0 0)) + ) + N(3 I(A2) + R(l11 (1520 1950) (0 0)) + ) + N(4 I(Q1) + R(l12 (1920 7070) (0 0)) + ) + N(5 I(Q2) + R(l12 (1940 1950) (0 0)) + ) + N(6 I(VSS) + R(l13 (2680 8390) (0 0)) + R(l13 (-30 -7640) (0 0)) + ) + P(1 I(VDD)) + P(2 I(A1)) + P(3 I(A2)) + P(4 I(Q1)) + P(5 I(Q2)) + P(6 I(VSS)) + X(1 INV M O(180) Y(1500 800) + P(0 6) + P(1 1) + P(2 3) + P(3 5) + ) + X(2 INV O(180) Y(1500 8400) + P(0 6) + P(1 1) + P(2 2) + P(3 4) + ) + ) + X(INVCHAIN + R((-915 -15) (10415 9215)) + N(1 + R(l3 (7340 1650) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (1625 1835) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(3 I(IN) + R(l3 (-90 6850) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(4 I(IN2) + R(l3 (5665 6790) (1590 650)) + R(l11 (-700 -350) (0 0)) + ) + N(5 I('VSS,VSS2,VSS2B') + R(l10 (-915 5290) (250 2960)) + R(l10 (-250 0) (915 250)) + R(l10 (-915 -7825) (915 250)) + R(l10 (-915 0) (250 3145)) + R(l13 (155 4305) (0 0)) + R(l13 (8990 -255) (0 0)) + R(l13 (25 -7115) (0 0)) + ) + N(6 I(OUT) + R(l12 (1890 2105) (0 0)) + ) + N(7 I(OUT2) + R(l12 (7730 2155) (0 0)) + ) + N(8 I(VDD) + R(l13 (8035 4540) (0 0)) + R(l13 (-5735 60) (0 0)) + ) + P(3 I(IN)) + P(4 I(IN2)) + P(5 I('VSS,VSS2,VSS2B')) + P(6 I(OUT)) + P(7 I(OUT2)) + P(8 I(VDD)) + X(1 INV2 Y(5780 -15) + P(0 8) + P(1 4) + P(2 1) + P(3 1) + P(4 7) + P(5 5) + ) + X(2 INV2 Y(0 0) + P(0 8) + P(1 3) + P(2 2) + P(3 2) + P(4 6) + P(5 5) + ) + ) + X(TOP + R((-305 350) (15415 9225)) + N(1 + R(l3 (12950 2130) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 + R(l3 (12100 7300) (640 530)) + R(l7 (-540 -415) (270 250)) + R(l8 (-1695 -250) (1695 250)) + R(l8 (-4075 -5650) (2630 250)) + R(l8 (-250 0) (250 5150)) + ) + N(3 + R(l7 (6465 7325) (220 240)) + R(l8 (-4100 -5365) (3125 250)) + R(l8 (-250 0) (250 4860)) + R(l8 (-250 0) (1225 250)) + ) + N(4 I(VSSTOP) + R(l10 (3610 8300) (2815 440)) + R(l10 (-710 -250) (0 0)) + R(l10 (3675 -165) (1975 565)) + R(l10 (-1975 -8190) (1975 575)) + R(l10 (-1005 -255) (0 0)) + ) + N(5 I(A) + R(l11 (975 7530) (0 0)) + ) + N(6 I(Q) + R(l12 (13260 2010) (0 0)) + ) + N(7 I(VDD) + R(l10 (2595 4805) (0 0)) + R(l10 (4295 -30) (0 0)) + R(l10 (4975 -50) (0 0)) + ) + P(4 I(VSSTOP)) + P(5 I(A)) + P(6 I(Q)) + P(7 I(VDD)) + X(1 INV2 Y(11365 375) + P(0 7) + P(1 2) + P(2 1) + P(3 1) + P(4 6) + P(5 4) + ) + X(2 INVCHAIN Y(610 365) + P(0 5) + P(1 3) + P(2 4) + P(3 3) + P(4 2) + P(5 7) + ) + ) +) +H( + K(PMOS MOS3) + K(NMOS MOS3) + X(INV + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I(Q)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A)) + P(4 I(Q)) + D(1 PMOS + I($1) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 1) + T(G 3) + T(D 4) + ) + D(2 NMOS + I($3) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 2) + T(G 3) + T(D 4) + ) + ) + X(INV2 + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + ) + X(2 INV I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + ) + ) + X(INVCHAIN + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + N(7 I('1')) + N(8 I('2')) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV2 I($2) + P(0 1) + P(1 2) + P(2 3) + P(3 7) + P(4 7) + P(5 4) + ) + X(2 INV2 I($3) + P(0 1) + P(1 2) + P(2 5) + P(3 8) + P(4 8) + P(5 6) + ) + ) + X(TOP + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I('1')) + N(5 I('3')) + N(6 I('2')) + N(7 I(Q)) + X(1 INVCHAIN I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + P(4 4) + P(5 5) + ) + X(2 INV2 I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + P(4 6) + P(5 7) + ) + ) +) +Z( + X(INV INV 1 + Z( + N(3 3 1) + N(4 4 1) + N(2 1 1) + N(1 2 1) + P(2 2 1) + P(3 3 1) + P(1 0 1) + P(0 1 1) + D(2 2 1) + D(1 1 1) + ) + ) + X(INV2 INV2 1 + Z( + N(2 3 1) + N(3 5 1) + N(4 4 1) + N(5 6 1) + N(1 1 1) + N(6 2 1) + P(1 2 1) + P(2 4 1) + P(3 3 1) + P(4 5 1) + P(0 0 1) + P(5 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(INVCHAIN INVCHAIN 1 + L( + M(W B('Matching nets OUT vs. Q1 from an ambiguous group of nets')) + M(W B('Matching nets OUT2 vs. Q2 from an ambiguous group of nets')) + M(I B('Matching nets $2 vs. 1 following an ambiguous match')) + M(I B('Matching nets IN vs. A1 following an ambiguous match')) + M(I B('Matching nets $1 vs. 2 following an ambiguous match')) + M(I B('Matching nets IN2 vs. A2 following an ambiguous match')) + ) + Z( + N(2 7 1) + N(1 8 1) + N(3 3 1) + N(4 5 1) + N(6 4 W) + N(7 6 W) + N(8 1 1) + N(5 2 1) + P(0 2 1) + P(1 4 1) + P(3 3 1) + P(4 5 1) + P(5 0 1) + P(2 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(TOP TOP 1 + Z( + N(3 4 1) + N(1 6 1) + N(2 5 1) + N(5 3 1) + N(6 7 1) + N(7 1 1) + N(4 2 1) + P(1 () 1) + P(2 () 1) + P(3 () 1) + P(0 () 1) + X(1 2 1) + X(2 1 1) + ) + ) +) diff --git a/testdata/lvs/must_connect2.sch b/testdata/lvs/must_connect2.sch new file mode 100644 index 000000000..9114c6de9 --- /dev/null +++ b/testdata/lvs/must_connect2.sch @@ -0,0 +1,24 @@ + +.SUBCKT TOP +X$1 VDD VSS A 1 1 3 INVCHAIN +X$2 VDD VSS 3 2 2 Q INV2 +.ENDS TOP + +* cell INVCHAIN +.SUBCKT INVCHAIN VDD VSS A1 Q1 A2 Q2 +X$2 VDD VSS A1 1 1 Q1 INV2 +X$3 VDD VSS A2 2 2 Q2 INV2 +.ENDS INVCHAIN + +* cell INV2 +.SUBCKT INV2 VDD VSS A1 Q1 A2 Q2 +X$1 VDD VSS A1 Q1 INV +X$2 VDD VSS A2 Q2 INV +.ENDS INV2 + +* cell INV +.SUBCKT INV VDD VSS A Q +M$1 VDD A Q VDD PMOS L=0.25U W=0.95U +M$3 VSS A Q VSS NMOS L=0.25U W=0.95U +.ENDS INV + diff --git a/testdata/lvs/ringo_simple_dmos.lvsdb.1 b/testdata/lvs/ringo_simple_dmos.lvsdb.1 index 59c0c5194..155a4128c 100644 --- a/testdata/lvs/ringo_simple_dmos.lvsdb.1 +++ b/testdata/lvs/ringo_simple_dmos.lvsdb.1 @@ -832,6 +832,9 @@ reference( # Cross reference xref( + log( + entry(error description('Circuits RINGO and RINGO could not be compared because the following subcircuits failed to compare:\n A: ND2X1\n B: ND2X1')) + ) circuit(INVX1 INVX1 match xref( net(4 4 match) diff --git a/testdata/lvs/ringo_simple_dmos.lvsdb.2 b/testdata/lvs/ringo_simple_dmos.lvsdb.2 index 94072e582..64a000533 100644 --- a/testdata/lvs/ringo_simple_dmos.lvsdb.2 +++ b/testdata/lvs/ringo_simple_dmos.lvsdb.2 @@ -832,6 +832,9 @@ reference( # Cross reference xref( + log( + entry(error description('Circuits RINGO and RINGO could not be compared because the following subcircuits failed to compare:\n A: ND2X1\n B: ND2X1')) + ) circuit(INVX1 INVX1 match xref( net(4 4 match) diff --git a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 index 179fe4455..576801b98 100644 --- a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 +++ b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 @@ -38,6 +38,9 @@ layout( global(l7 SUBSTRATE) global(l10 SUBSTRATE) + # Log entries + message(warning description('Must-connect nets VDD must be connected further up in the hierarchy - this is an error at chip top level') cell(RINGO) cat('must-connect')) + # Device class section class(PMOS MOS4) class(NMOS MOS4) @@ -404,36 +407,36 @@ layout( ) net(12 name(VDD) rect(l3 (22600 4500) (1400 3500)) - rect(l3 (-23500 -3500) (1400 3500)) - rect(l3 (-1900 -3500) (600 3500)) - rect(l3 (25800 -3500) (1400 3500)) + rect(l3 (2400 -3500) (1400 3500)) rect(l3 (-100 -3500) (600 3500)) - rect(l8 (-5090 -1240) (180 180)) + rect(l3 (-27800 -3500) (1400 3500)) + rect(l3 (-1900 -3500) (600 3500)) + rect(l8 (22610 -1240) (180 180)) rect(l8 (-180 370) (180 180)) rect(l8 (-180 -1280) (180 180)) - rect(l8 (-22280 370) (180 180)) + rect(l8 (3620 370) (180 180)) rect(l8 (-180 370) (180 180)) rect(l8 (-180 -1280) (180 180)) - rect(l8 (25720 370) (180 180)) + rect(l8 (-26080 370) (180 180)) rect(l8 (-180 370) (180 180)) rect(l8 (-180 -1280) (180 180)) - rect(l11 (-4890 1010) (0 0)) + rect(l11 (21010 1010) (0 0)) rect(l11 (2800 -50) (0 0)) rect(l11 (-22150 -100) (0 0)) rect(l11 (19750 -450) (1200 800)) rect(l11 (-750 -1450) (300 1400)) rect(l11 (-100 -350) (0 0)) - rect(l11 (-22750 -400) (1200 800)) - rect(l11 (-750 -1450) (300 1400)) - rect(l11 (-100 -350) (0 0)) - rect(l11 (-1250 -400) (600 800)) - rect(l11 (25900 -800) (1200 800)) + rect(l11 (3150 -400) (1200 800)) rect(l11 (-750 -1450) (300 1400)) rect(l11 (-100 -350) (0 0)) rect(l11 (550 -400) (600 800)) - rect(l9 (-5250 -1500) (500 1500)) - rect(l9 (-22600 -1500) (500 1500)) - rect(l9 (25400 -1500) (500 1500)) + rect(l11 (-27700 -800) (1200 800)) + rect(l11 (-750 -1450) (300 1400)) + rect(l11 (-100 -350) (0 0)) + rect(l11 (-1250 -400) (600 800)) + rect(l9 (22450 -1500) (500 1500)) + rect(l9 (3300 -1500) (500 1500)) + rect(l9 (-26400 -1500) (500 1500)) ) net(13 name(OUT) rect(l11 (25990 3840) (320 320)) @@ -448,32 +451,32 @@ layout( rect(l13 (-150 -200) (400 400)) ) net(15 name(VSS) - rect(l8 (27010 1610) (180 180)) + rect(l8 (1110 1610) (180 180)) rect(l8 (-180 -1280) (180 180)) rect(l8 (-180 370) (180 180)) - rect(l8 (-3980 370) (180 180)) + rect(l8 (21920 370) (180 180)) rect(l8 (-180 -1280) (180 180)) rect(l8 (-180 370) (180 180)) - rect(l8 (-22280 370) (180 180)) + rect(l8 (3620 370) (180 180)) rect(l8 (-180 -1280) (180 180)) rect(l8 (-180 370) (180 180)) - rect(l11 (24710 -290) (0 0)) - rect(l11 (-3850 0) (0 0)) - rect(l11 (-19200 -100) (0 0)) - rect(l11 (24000 -400) (300 1400)) - rect(l11 (-750 -1450) (1200 800)) - rect(l11 (-550 -400) (0 0)) - rect(l11 (550 -400) (600 800)) - rect(l11 (-5150 -750) (300 1400)) - rect(l11 (-750 -1450) (1200 800)) - rect(l11 (-550 -400) (0 0)) - rect(l11 (-22300 -350) (300 1400)) + rect(l11 (-24240 -390) (0 0)) + rect(l11 (19200 100) (0 0)) + rect(l11 (3850 0) (0 0)) + rect(l11 (-24950 -500) (300 1400)) rect(l11 (-750 -1450) (1200 800)) rect(l11 (-550 -400) (0 0)) rect(l11 (-1250 -400) (600 800)) - rect(l10 (26250 -800) (500 1500)) - rect(l10 (-4300 -1500) (500 1500)) - rect(l10 (-22600 -1500) (500 1500)) + rect(l11 (22550 -750) (300 1400)) + rect(l11 (-750 -1450) (1200 800)) + rect(l11 (-550 -400) (0 0)) + rect(l11 (3600 -350) (300 1400)) + rect(l11 (-750 -1450) (1200 800)) + rect(l11 (-550 -400) (0 0)) + rect(l11 (550 -400) (600 800)) + rect(l10 (-27350 -800) (500 1500)) + rect(l10 (21600 -1500) (500 1500)) + rect(l10 (3300 -1500) (500 1500)) ) # Outgoing pins and their connections to nets diff --git a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 index 6ae8d34f4..86c5148b8 100644 --- a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 +++ b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 @@ -38,6 +38,9 @@ layout( global(l7 SUBSTRATE) global(l10 SUBSTRATE) + # Log entries + message(warning description('Must-connect nets VDD must be connected further up in the hierarchy - this is an error at chip top level') cell(RINGO) cat('must-connect')) + # Device class section class(PMOS MOS4) class(NMOS MOS4) @@ -404,36 +407,36 @@ layout( ) net(12 name(VDD) rect(l3 (22600 4500) (1400 3500)) - rect(l3 (-23500 -3500) (1400 3500)) - rect(l3 (-1900 -3500) (600 3500)) - rect(l3 (25800 -3500) (1400 3500)) + rect(l3 (2400 -3500) (1400 3500)) rect(l3 (-100 -3500) (600 3500)) - rect(l8 (-5090 -1240) (180 180)) + rect(l3 (-27800 -3500) (1400 3500)) + rect(l3 (-1900 -3500) (600 3500)) + rect(l8 (22610 -1240) (180 180)) rect(l8 (-180 370) (180 180)) rect(l8 (-180 -1280) (180 180)) - rect(l8 (-22280 370) (180 180)) + rect(l8 (3620 370) (180 180)) rect(l8 (-180 370) (180 180)) rect(l8 (-180 -1280) (180 180)) - rect(l8 (25720 370) (180 180)) + rect(l8 (-26080 370) (180 180)) rect(l8 (-180 370) (180 180)) rect(l8 (-180 -1280) (180 180)) - rect(l11 (-4890 1010) (0 0)) + rect(l11 (21010 1010) (0 0)) rect(l11 (2800 -50) (0 0)) rect(l11 (-22150 -100) (0 0)) rect(l11 (19750 -450) (1200 800)) rect(l11 (-750 -1450) (300 1400)) rect(l11 (-100 -350) (0 0)) - rect(l11 (-22750 -400) (1200 800)) - rect(l11 (-750 -1450) (300 1400)) - rect(l11 (-100 -350) (0 0)) - rect(l11 (-1250 -400) (600 800)) - rect(l11 (25900 -800) (1200 800)) + rect(l11 (3150 -400) (1200 800)) rect(l11 (-750 -1450) (300 1400)) rect(l11 (-100 -350) (0 0)) rect(l11 (550 -400) (600 800)) - rect(l9 (-5250 -1500) (500 1500)) - rect(l9 (-22600 -1500) (500 1500)) - rect(l9 (25400 -1500) (500 1500)) + rect(l11 (-27700 -800) (1200 800)) + rect(l11 (-750 -1450) (300 1400)) + rect(l11 (-100 -350) (0 0)) + rect(l11 (-1250 -400) (600 800)) + rect(l9 (22450 -1500) (500 1500)) + rect(l9 (3300 -1500) (500 1500)) + rect(l9 (-26400 -1500) (500 1500)) ) net(13 name(OUT) rect(l11 (25990 3840) (320 320)) @@ -448,32 +451,32 @@ layout( rect(l13 (-150 -200) (400 400)) ) net(15 name(VSS) - rect(l8 (27010 1610) (180 180)) + rect(l8 (1110 1610) (180 180)) rect(l8 (-180 -1280) (180 180)) rect(l8 (-180 370) (180 180)) - rect(l8 (-3980 370) (180 180)) + rect(l8 (21920 370) (180 180)) rect(l8 (-180 -1280) (180 180)) rect(l8 (-180 370) (180 180)) - rect(l8 (-22280 370) (180 180)) + rect(l8 (3620 370) (180 180)) rect(l8 (-180 -1280) (180 180)) rect(l8 (-180 370) (180 180)) - rect(l11 (24710 -290) (0 0)) - rect(l11 (-3850 0) (0 0)) - rect(l11 (-19200 -100) (0 0)) - rect(l11 (24000 -400) (300 1400)) - rect(l11 (-750 -1450) (1200 800)) - rect(l11 (-550 -400) (0 0)) - rect(l11 (550 -400) (600 800)) - rect(l11 (-5150 -750) (300 1400)) - rect(l11 (-750 -1450) (1200 800)) - rect(l11 (-550 -400) (0 0)) - rect(l11 (-22300 -350) (300 1400)) + rect(l11 (-24240 -390) (0 0)) + rect(l11 (19200 100) (0 0)) + rect(l11 (3850 0) (0 0)) + rect(l11 (-24950 -500) (300 1400)) rect(l11 (-750 -1450) (1200 800)) rect(l11 (-550 -400) (0 0)) rect(l11 (-1250 -400) (600 800)) - rect(l10 (26250 -800) (500 1500)) - rect(l10 (-4300 -1500) (500 1500)) - rect(l10 (-22600 -1500) (500 1500)) + rect(l11 (22550 -750) (300 1400)) + rect(l11 (-750 -1450) (1200 800)) + rect(l11 (-550 -400) (0 0)) + rect(l11 (3600 -350) (300 1400)) + rect(l11 (-750 -1450) (1200 800)) + rect(l11 (-550 -400) (0 0)) + rect(l11 (550 -400) (600 800)) + rect(l10 (-27350 -800) (500 1500)) + rect(l10 (21600 -1500) (500 1500)) + rect(l10 (3300 -1500) (500 1500)) ) # Outgoing pins and their connections to nets diff --git a/testdata/lvs/test_22a.lvsdb.1 b/testdata/lvs/test_22a.lvsdb.1 index a09d71fe2..ed14452f3 100644 --- a/testdata/lvs/test_22a.lvsdb.1 +++ b/testdata/lvs/test_22a.lvsdb.1 @@ -70,6 +70,9 @@ layout( global(l1 vss) global(l6 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22a.lvsdb.2 b/testdata/lvs/test_22a.lvsdb.2 index b2354cc21..653bea80f 100644 --- a/testdata/lvs/test_22a.lvsdb.2 +++ b/testdata/lvs/test_22a.lvsdb.2 @@ -70,6 +70,9 @@ layout( global(l1 vss) global(l6 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22b.lvsdb.1 b/testdata/lvs/test_22b.lvsdb.1 index 5b71fbe2d..37f240e44 100644 --- a/testdata/lvs/test_22b.lvsdb.1 +++ b/testdata/lvs/test_22b.lvsdb.1 @@ -70,6 +70,9 @@ layout( global(l1 vss) global(l6 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22b.lvsdb.2 b/testdata/lvs/test_22b.lvsdb.2 index 03b56d2fd..806634244 100644 --- a/testdata/lvs/test_22b.lvsdb.2 +++ b/testdata/lvs/test_22b.lvsdb.2 @@ -70,6 +70,9 @@ layout( global(l1 vss) global(l6 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22c.lvsdb.1 b/testdata/lvs/test_22c.lvsdb.1 index 3daddbd99..f61eaa9e9 100644 --- a/testdata/lvs/test_22c.lvsdb.1 +++ b/testdata/lvs/test_22c.lvsdb.1 @@ -70,6 +70,9 @@ layout( global(l6 vss) global(l1 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22c.lvsdb.2 b/testdata/lvs/test_22c.lvsdb.2 index 4e459b3fe..c85f87ba1 100644 --- a/testdata/lvs/test_22c.lvsdb.2 +++ b/testdata/lvs/test_22c.lvsdb.2 @@ -70,6 +70,9 @@ layout( global(l6 vss) global(l1 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22c.lvsdb.3 b/testdata/lvs/test_22c.lvsdb.3 index 66213ecfc..2fbe22102 100644 --- a/testdata/lvs/test_22c.lvsdb.3 +++ b/testdata/lvs/test_22c.lvsdb.3 @@ -70,6 +70,9 @@ layout( global(l6 vss) global(l1 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22d.lvsdb.1 b/testdata/lvs/test_22d.lvsdb.1 index 453674e0e..45e3e88fc 100644 --- a/testdata/lvs/test_22d.lvsdb.1 +++ b/testdata/lvs/test_22d.lvsdb.1 @@ -70,6 +70,9 @@ layout( global(l6 vss) global(l1 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22d.lvsdb.2 b/testdata/lvs/test_22d.lvsdb.2 index 253f1dbb1..22c7b9fa6 100644 --- a/testdata/lvs/test_22d.lvsdb.2 +++ b/testdata/lvs/test_22d.lvsdb.2 @@ -70,6 +70,9 @@ layout( global(l6 vss) global(l1 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/lvs/test_22d.lvsdb.3 b/testdata/lvs/test_22d.lvsdb.3 index a0c293345..5c772a9c5 100644 --- a/testdata/lvs/test_22d.lvsdb.3 +++ b/testdata/lvs/test_22d.lvsdb.3 @@ -70,6 +70,9 @@ layout( global(l6 vss) global(l1 vss) + # Log entries + message(warning description('Must-connect nets vdd must be connected further up in the hierarchy - this is an error at chip top level') cell(SP6TArray_2X4) cat('must-connect')) + # Device class section class(active_res RES) class(poly_res RES) diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index c40e91b9b..e5404b547 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -1011,6 +1011,20 @@ END end + def test_21_LogAPI + + l2n = RBA::LayoutToNetlist::new + l2n.read(File.join($ut_testsrc, "testdata", "algo", "l2n_reader_au_6.l2n")) + + le = l2n.each_log_entry.collect { |s| s.to_s } + assert_equal(le.size, 4) + assert_equal(le[0].to_s, "info") + assert_equal(le[1].to_s, "[cat description] In cell cell_name: info, shape: (1,1;2,2;3,1)") + assert_equal(le[2].to_s, "warning") + assert_equal(le[3].to_s, "error") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbLogTest.rb b/testdata/ruby/dbLogTest.rb new file mode 100644 index 000000000..f53e00f73 --- /dev/null +++ b/testdata/ruby/dbLogTest.rb @@ -0,0 +1,59 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2023 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 DBLog_TestClass < TestBase + + def test_1_Log + + assert_equal(RBA::LogEntryData::NoSeverity.to_s, "NoSeverity") + assert_equal(RBA::LogEntryData::Info.to_s, "Info") + assert_equal(RBA::LogEntryData::Error.to_s, "Error") + assert_equal(RBA::LogEntryData::Warning.to_s, "Warning") + + le = RBA::LogEntryData::new + + le.message = "message" + assert_equal(le.message, "message") + + le.geometry = RBA::DPolygon::new(RBA::DBox::new(1, 2, 3, 4)) + assert_equal(le.geometry.to_s, "(1,2;1,4;3,4;3,2)") + + le.category_name = "42" + assert_equal(le.category_name, "42") + + le.category_description = "the answer" + assert_equal(le.category_description, "the answer") + + le.cell_name = "TOP" + assert_equal(le.cell_name, "TOP") + + assert_equal(le.to_s, "[the answer] In cell TOP: message, shape: (1,2;1,4;3,4;3,2)") + + end + +end + +load("test_epilogue.rb") diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 4c5b9fd83..738b57433 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -1120,11 +1120,11 @@ END c.join_nets(c.net_by_name("IN"), c.net_by_name("OUT")) assert_equal(nl.to_s, <<"END") -circuit INV2 (IN=IN,$2=$2,OUT=IN,$4=$4,$5=$5); - subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN); - subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN); - subcircuit PTRANS SC3 ($1=$5,$2=IN,$3=$2); - subcircuit NTRANS SC4 ($1=$4,$2=IN,$3=$2); +circuit INV2 ('IN,OUT'='IN,OUT',$2=$2,$3=$4,$4=$5); + subcircuit PTRANS SC1 ($1=$5,$2=$2,$3='IN,OUT'); + subcircuit NTRANS SC2 ($1=$4,$2=$2,$3='IN,OUT'); + subcircuit PTRANS SC3 ($1=$5,$2='IN,OUT',$3=$2); + subcircuit NTRANS SC4 ($1=$4,$2='IN,OUT',$3=$2); end; circuit PTRANS ($1=$1,$2=$2,$3=$3); device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index c3b58c824..6a447e9f0 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -181,6 +181,12 @@ class NetlistCompare_TestClass < TestBase def test_1 + # severity enums + assert_equal(NetlistCompareTestLogger::Info.to_s, "Info") + assert_equal(NetlistCompareTestLogger::Error.to_s, "Error") + assert_equal(NetlistCompareTestLogger::Warning.to_s, "Warning") + assert_equal(NetlistCompareTestLogger::NoSeverity.to_s, "NoSeverity") + nl1 = RBA::Netlist::new nl2 = RBA::Netlist::new dc = RBA::DeviceClass::new diff --git a/testdata/ruby/dbNetlistDeviceExtractors.rb b/testdata/ruby/dbNetlistDeviceExtractors.rb index 5caa5ff90..d4d7023a3 100644 --- a/testdata/ruby/dbNetlistDeviceExtractors.rb +++ b/testdata/ruby/dbNetlistDeviceExtractors.rb @@ -27,7 +27,7 @@ class DBNetlistExtractorTests_TestClass < TestBase def test_1_Error - err = RBA::NetlistDeviceExtractorError::new + err = RBA::LogEntryData::new err.message = "MSG" err.cell_name = "Cell"