Merge pull request #1491 from KLayout/issue-885

Issue 885
This commit is contained in:
Matthias Köfferlein 2023-11-07 21:35:04 +01:00 committed by GitHub
commit 8d51124fd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
109 changed files with 4676 additions and 1318 deletions

View File

@ -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 \

View File

@ -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<std::string> 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);

View File

@ -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 ();

View File

@ -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<unsigned int> ());
@ -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<std::string, std::list<tl::GlobPattern> > jp_per_cell;
for (std::list<std::pair<tl::GlobPattern, tl::GlobPattern> >::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<std::string, std::list<tl::GlobPattern> >::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<std::string, std::list<std::set<std::string> > > jn_per_cell;
for (std::list<std::pair<tl::GlobPattern, std::set<std::string> > >::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<std::string, std::list<std::set<std::string> > >::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<std::string, std::vector<db::Net *> > 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<std::string> &p)
{
// NOTE: this version implies implicit joining of different nets with the same name from the set p
std::vector<db::Net *> 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<db::Net *> &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) {

View File

@ -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<unsigned int, std::string>::const_iterator layer_iterator;
typedef std::vector<db::LogEntryData> 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<std::string, db::ShapeCollection *> &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<db::DeepShapeStore> mp_internal_dss;
tl::weak_ptr<db::DeepShapeStore> 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<tl::GlobPattern> m_joined_net_names;
std::list<std::pair<tl::GlobPattern, tl::GlobPattern> > m_joined_net_names_per_cell;
std::list<std::set<std::string> > 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<Net *> &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<std::string> &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);

View File

@ -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");
}
}

View File

@ -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(<number>) - file format version [short key: V]
@ -144,7 +144,7 @@ namespace db
* coordinates are bottom/left and top/right
*
* [text]:
* text(<layer> [text] [coord]) - defines a rectangle [short key: J]
* text(<layer> <string> [coord]) - defines a label [short key: J]
*
* [coord]:
* <x> <y> - absolute coordinates
@ -181,6 +181,26 @@ namespace db
* mirror - if specified, the instance is mirrored before rotation [short key: M]
* scale(<mag>) - 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(<name>) - message text [short key: B]
*
* [message-geometry]:
* polygon(<string>) - message geometry polygon in string-serialized form [short key: Q]
*
* [message-cell]:
* cell(<name>) - message cell [short key: C]
*
* [message-category]:
* cat(<name> <name>?) - message category with optional description [short key: X]
*
* [severity]:
* info | - [short key: I]
* warning | - [short key: W]
* error - [short key: E]
*
* [any]:
* * |
* <token> |
@ -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 <bool Short> struct DB_PUBLIC keys;

View File

@ -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);

View File

@ -109,6 +109,8 @@ protected:
void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, Brace *nested = 0, std::map<const db::Circuit *, ObjectMap> *map_per_circuit = 0);
static size_t terminal_id (const db::DeviceClass *device_class, const std::string &tname);
static std::pair<db::DeviceAbstract *, const db::DeviceClass *> 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<db::CellInstArray, std::list<Connections> > &connections);
@ -151,15 +162,10 @@ protected:
db::Box read_rect ();
void read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster<NetShape> &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);
};
}

View File

@ -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 <class T, class Tr>
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<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, co
m_progress.set_unit (1024 * 1024);
}
template <class Keys>
std::string std_writer_impl<Keys>::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 <class Keys>
std::string std_writer_impl<Keys>::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 <class Keys>
void std_writer_impl<Keys>::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<Keys>::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<Keys>::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 <class T, class Tr>
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 <class Keys>
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Circuit &circuit, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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(<name>) - 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 <bool Short> struct DB_PUBLIC keys;

View File

@ -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 ();
}

View File

@ -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);

View File

@ -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<const db::Circuit *, std::map<const db::Net *, unsigned int> > 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<const db::Pin
}
}
template <class Keys>
std::string std_writer_impl<Keys>::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 <class Keys>
std::string std_writer_impl<Keys>::status_to_s (const db::NetlistCrossReference::Status status)
{
@ -206,30 +194,31 @@ std::string std_writer_impl<Keys>::status_to_s (const db::NetlistCrossReference:
}
}
template <class Keys>
std::string std_writer_impl<Keys>::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 <class Keys>
void std_writer_impl<Keys>::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<Keys>::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<Keys>::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<const db::Pin *, unsigned int> pin2index_a, pin2index_b;
@ -257,15 +249,15 @@ void std_writer_impl<Keys>::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);
}
}

188
src/db/db/dbLog.cc Normal file
View File

@ -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<std::string> m_strings;
std::map<std::string, size_t> 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;
}
}

178
src/db/db/dbLog.h Normal file
View File

@ -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 <string>
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

View File

@ -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;
}
};
/**

View File

@ -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 ()));
}
}
}

View File

@ -25,6 +25,7 @@
#include "dbCommon.h"
#include "dbNetlist.h"
#include "dbLog.h"
#include <set>
#include <map>
@ -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

View File

@ -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::multimap<size_t, const NetGraphNode
double fuzz_factor = double (fuzz) / ne;
if (fuzz_factor < max_fuzz_factor) {
std::string msg = tl::to_string (tr ("Net %s from netlist approximately matches net %s from reference netlist (fuzziness %d nodes)"));
logger->log_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::multimap<size_t, const NetGraphNode
size_t fuzz = distance3 (*i->second, *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<NodeEdgePa
if (max_depth != std::numeric_limits<size_t>::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<NodeEdgePa
} else if (max_n_branch != std::numeric_limits<size_t>::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.";

View File

@ -414,8 +414,8 @@ generic_categorizer<Obj>::cat_for (const Obj *cls)
}
// explicit instantiations
template class DB_PUBLIC generic_categorizer<db::DeviceClass>;
template class DB_PUBLIC generic_categorizer<db::Circuit>;
template class generic_categorizer<db::DeviceClass>;
template class generic_categorizer<db::Circuit>;
// --------------------------------------------------------------------------------------------------------------------
// DeviceCategorizer implementation

View File

@ -27,6 +27,7 @@ namespace db
{
NetlistCrossReference::NetlistCrossReference ()
: mp_per_circuit_data (0)
{
// .. nothing yet ..
}

View File

@ -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) { }

View File

@ -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 ();
}
}

View File

@ -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<db::NetlistDeviceExtractorError> error_list;
typedef error_list::const_iterator error_iterator;
typedef std::list<db::LogEntryData> log_entry_list;
typedef log_entry_list::const_iterator log_entry_iterator;
typedef std::vector<db::NetlistDeviceExtractorLayerDefinition> layer_definitions;
typedef layer_definitions::const_iterator layer_definitions_iterator;
typedef std::map<std::string, db::ShapeCollection *> 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<unsigned int> m_layers;
error_list m_errors;
log_entry_list m_log_entries;
std::map<size_t, std::pair<db::Device *, geometry_per_terminal_type> > m_new_devices;
std::map<DeviceCellKey, std::pair<db::cell_index_type, db::DeviceAbstract *> > m_device_cells;

View File

@ -136,11 +136,11 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
rdiff2gate.set_base_verbosity (rdiff.base_verbosity ());
if (rdiff2gate.empty ()) {
error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
warn (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
} else {
if (rdiff2gate.count () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting with one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.count ())), *p);
warn (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting with one gate shape (found %d) - gate shape ignored")), int (rdiff2gate.count ())), *p);
continue;
}
@ -158,7 +158,7 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
db::Edges edges (rgate.edges () & db::Edges (*d2g));
db::Edges::length_type l = edges.length ();
if (l == 0) {
error (tl::to_string (tr ("Vanishing edges for interaction gate/diff (corner interaction) - gate shape ignored")));
warn (tl::to_string (tr ("Vanishing edges for interaction gate/diff (corner interaction) - gate shape ignored")));
} else {
widths.push_back (l);
}
@ -237,18 +237,18 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
ddiff2gate.set_base_verbosity (ddiff.base_verbosity ());
if (sdiff2gate.empty () && ddiff2gate.empty ()) {
error (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
warn (tl::to_string (tr ("Gate shape touches no diffusion - ignored")), *p);
} else if (sdiff2gate.empty () || ddiff2gate.empty ()) {
error (tl::to_string (tr ("Gate shape touches a single diffusion only - ignored")), *p);
warn (tl::to_string (tr ("Gate shape touches a single diffusion only - ignored")), *p);
} else {
if (sdiff2gate.count () != 1) {
error (tl::sprintf (tl::to_string (tr ("Expected one polygons on source diff interacting with one gate shape (found %d) - gate shape ignored")), int (sdiff2gate.count ())), *p);
warn (tl::sprintf (tl::to_string (tr ("Expected one polygons on source diff interacting with one gate shape (found %d) - gate shape ignored")), int (sdiff2gate.count ())), *p);
continue;
}
if (ddiff2gate.count () != 1) {
error (tl::sprintf (tl::to_string (tr ("Expected one polygons on drain diff interacting with one gate shape (found %d) - gate shape ignored")), int (ddiff2gate.count ())), *p);
warn (tl::sprintf (tl::to_string (tr ("Expected one polygons on drain diff interacting with one gate shape (found %d) - gate shape ignored")), int (ddiff2gate.count ())), *p);
continue;
}
@ -258,7 +258,7 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
db::Edges edges (rgate.edges () & sdiff2gate.edges ());
sdwidth = edges.length ();
if (sdwidth == 0) {
error (tl::to_string (tr ("Vanishing edges for interaction gate/source diff (corner interaction) - gate shape ignored")));
warn (tl::to_string (tr ("Vanishing edges for interaction gate/source diff (corner interaction) - gate shape ignored")));
continue;
}
}
@ -267,7 +267,7 @@ void NetlistDeviceExtractorMOS3Transistor::extract_devices (const std::vector<db
db::Edges edges (rgate.edges () & ddiff2gate.edges ());
ddwidth = edges.length ();
if (ddwidth == 0) {
error (tl::to_string (tr ("Vanishing edges for interaction gate/drain diff (corner interaction) - gate shape ignored")));
warn (tl::to_string (tr ("Vanishing edges for interaction gate/drain diff (corner interaction) - gate shape ignored")));
continue;
}
}
@ -438,7 +438,7 @@ void NetlistDeviceExtractorResistor::extract_devices (const std::vector<db::Regi
db::Region contacts_per_res = contact_wo_res.selected_interacting (rres);
if (contacts_per_res.count () != 2) {
error (tl::sprintf (tl::to_string (tr ("Expected two polygons on contacts interacting with one resistor shape (found %d) - resistor shape ignored")), int (contacts_per_res.count ())), *p);
warn (tl::sprintf (tl::to_string (tr ("Expected two polygons on contacts interacting with one resistor shape (found %d) - resistor shape ignored")), int (contacts_per_res.count ())), *p);
continue;
}
@ -458,7 +458,7 @@ void NetlistDeviceExtractorResistor::extract_devices (const std::vector<db::Regi
db::Coord width2 = eperp.length ();
if (width2 < 1) {
error (tl::to_string (tr ("Invalid contact geometry - resistor shape ignored")), *p);
warn (tl::to_string (tr ("Invalid contact geometry - resistor shape ignored")), *p);
continue;
}
@ -681,7 +681,7 @@ void NetlistDeviceExtractorBJT3Transistor::extract_devices (const std::vector<db
db::Region remitter2base = rbase & remitters;
if (remitter2base.empty ()) {
error (tl::to_string (tr ("Base shape without emitters - ignored")), *p);
warn (tl::to_string (tr ("Base shape without emitters - ignored")), *p);
} else {
// collectors inside base

View File

@ -178,6 +178,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
m_device_annot_name_id = mp_layout->properties_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<db::cell_index_type, tl::equivalence_clusters<size_t> > net_name_equivalence;
if (m_text_annot_name_id.first) {

View File

@ -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<tl::GlobPattern> &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<tl::GlobPattern> &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<std::set<std::string> > &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<std::set<std::string> > &jnn);

View File

@ -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) {

View File

@ -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
*/

View File

@ -1839,7 +1839,7 @@ gsi::EnumIn<db::Edges, db::SpecialEdgeOrientationFilter::FilterType> 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"
);

View File

@ -464,6 +464,20 @@ Class<db::LayoutToNetlist> 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<db::LayoutToNetlist> 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<db::LayoutToNetlist> 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<tl::Variant> (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"),
"@brief Runs an antenna check on the extracted clusters\n"
"\n"

117
src/db/db/gsiDeclDbLog.cc Normal file
View File

@ -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<db::LogEntryData> 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<db::Severity> 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<db::LogEntryData> inject_SeverityEnum_into_LogEntryData (decl_Severity.defs ());
}

View File

@ -1659,7 +1659,8 @@ Class<db::Circuit> 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"

View File

@ -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, db::NetlistCompareLogger::Severity, const std::string &> (&GenericNetlistCompareLogger::log_entry, severity, msg);
cb_log_entry.issue<GenericNetlistCompareLogger, db::Severity, const std::string &> (&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<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"This class has been introduced in version 0.26."
);
gsi::EnumIn<GenericNetlistCompareLogger, db::NetlistCompareLogger::Severity> 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<db::Severity> decl_Severity;
gsi::ClassExt<GenericNetlistCompareLogger> inject_SeverityEnum_into_GenericNetlistCompareLogger (decl_Severity.defs ());
}

View File

@ -156,64 +156,6 @@ Class<DeviceClassFactoryImpl> decl_dbDeviceClassFactoryBase ("db", "DeviceClassF
"This class has been introduced in version 0.27.3.\n"
);
Class<db::NetlistDeviceExtractorError> 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<db::NetlistDeviceExtractor> 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<GenericDeviceExtractor> 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"

View File

@ -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);
}

View File

@ -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<db::LayoutToNetlist> l2n_p (new db::LayoutToNetlist (db::RecursiveShapeIterator (m_ly, tc, std::set<unsigned int> ())));
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<const db::Region *, unsigned int> 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<const db::Region *, unsigned int> 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<db::Region> rbulk;
std::unique_ptr<db::Region> rnwell;
std::unique_ptr<db::Region> rnwell_lbl;
std::unique_ptr<db::Region> ractive;
std::unique_ptr<db::Region> rpplus;
std::unique_ptr<db::Region> rnplus;
std::unique_ptr<db::Region> rpoly;
std::unique_ptr<db::Texts> rpoly_lbl;
std::unique_ptr<db::Region> rdiff_cont;
std::unique_ptr<db::Region> rpoly_cont;
std::unique_ptr<db::Region> rmetal1;
std::unique_ptr<db::Texts> rmetal1_lbl;
std::unique_ptr<db::Region> rvia1;
std::unique_ptr<db::Region> rmetal2;
std::unique_ptr<db::Texts> 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<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().layer_map = test_rig.lmap ();
options.get_options<db::CommonReaderOptions> ().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<unsigned int> ()));
std::unique_ptr<db::LayoutToNetlist> l2n (test_rig.make_l2n ());
std::unique_ptr<db::Region> rbulk (l2n.make_layer ("bulk"));
std::unique_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
std::unique_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
std::unique_ptr<db::Region> rpplus (l2n.make_layer (pplus, "pplus"));
std::unique_ptr<db::Region> rnplus (l2n.make_layer (nplus, "nplus"));
std::unique_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
std::unique_ptr<db::Texts> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
std::unique_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
std::unique_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
std::unique_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
std::unique_ptr<db::Texts> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
std::unique_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
std::unique_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
std::unique_ptr<db::Texts> 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<const db::Region *, unsigned int> 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<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().layer_map = test_rig.lmap ();
options.get_options<db::CommonReaderOptions> ().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<unsigned int> ()));
std::unique_ptr<db::LayoutToNetlist> l2n (test_rig.make_l2n ());
std::unique_ptr<db::Region> rbulk (l2n.make_layer ("bulk"));
std::unique_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
std::unique_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
std::unique_ptr<db::Region> rpplus (l2n.make_layer (pplus, "pplus"));
std::unique_ptr<db::Region> rnplus (l2n.make_layer (nplus, "nplus"));
std::unique_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
std::unique_ptr<db::Texts> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
std::unique_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
std::unique_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
std::unique_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
std::unique_ptr<db::Texts> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
std::unique_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
std::unique_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
std::unique_ptr<db::Texts> 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<const db::Region *, unsigned int> 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<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().layer_map = test_rig.lmap ();
options.get_options<db::CommonReaderOptions> ().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<unsigned int> ()));
std::unique_ptr<db::LayoutToNetlist> l2n (test_rig.make_l2n ());
std::unique_ptr<db::Region> rbulk (l2n.make_layer ("bulk"));
std::unique_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
std::unique_ptr<db::Region> rnwell_lbl (l2n.make_layer (nwell_lbl, "nwell_lbl"));
std::unique_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
std::unique_ptr<db::Region> rpplus (l2n.make_layer (pplus, "pplus"));
std::unique_ptr<db::Region> rnplus (l2n.make_layer (nplus, "nplus"));
std::unique_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
std::unique_ptr<db::Texts> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
std::unique_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
std::unique_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
std::unique_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
std::unique_ptr<db::Texts> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
std::unique_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
std::unique_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
std::unique_ptr<db::Texts> 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<std::string> 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<const db::Region *, unsigned int> 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"

View File

@ -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<unsigned int> ()));
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);
}

View File

@ -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"));
}

View File

@ -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"
)
}

View File

@ -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"
);

View File

@ -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<db::NetlistDeviceExtractorError> errors (dummy_ex.begin_errors (), dummy_ex.end_errors ());
std::vector<db::LogEntryData> 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");
}

View File

@ -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"
);
}

View File

@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri)
SOURCES = \
dbCompoundOperationTests.cc \
dbFillToolTests.cc \
dbLogTests.cc \
dbRecursiveInstanceIteratorTests.cc \
dbRegionCheckUtilsTests.cc \
dbUtilsTests.cc \

View File

@ -102,19 +102,21 @@ connect(metal2, metal2_labels)</pre>
<p>
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.
</p>
<p>
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)</pre>
<p>
"connect_implicit" (see <a href="/about/drc_ref_global.xml#connect_implicit">connect_implicit</a>)
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.
</p>
<p>
You can declare the layout as being a top level one. This turns the
warning about missing physical connections into an error:
</p>
<pre>top_level(true)</pre>
<p>
The "connect_implicit" feature is also called "must connect" nets in other systems.
</p>
<p>
@ -142,7 +158,8 @@ connect(metal2, metal2_labels)</pre>
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",
<b>not</b> 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).
</p>
<p>
@ -151,30 +168,22 @@ connect(metal2, metal2_labels)</pre>
</p>
<p>
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.
</p>
<p>
The following statement will connect all nets labelled with "VDD"
from the "MEMCELL" subcell:
</p>
<pre>connect_implicit("MEMCELL", "VDD")</pre>
<p>
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.
</p>
<p>
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.
</p>
<p>
@ -217,20 +226,18 @@ connect(metal2, metal2_labels)</pre>
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
@ -242,17 +249,35 @@ connect(metal2, metal2_labels)</pre>
connect_explicit("INV", [ "BULK", "VSS" ])
</pre>
<p>
Note that this rule will form a new net called "BULK,VSS" combining both
subnets.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
Again, the "connect_explicit" statements must be given before the netlist is
extracted. Typically this happens before or shortly after "connect"
statements.
</p>
</doc>

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -40,6 +40,7 @@
#include "dbCellMapping.h"
#include "dbLayerMapping.h"
#include "dbCell.h"
#include "dbLog.h"
#include <QUrl>
#include <QPainter>
@ -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<NetlistLogModel *> (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<db::LayoutVsSchematic *> (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<bool, db::DCplxTrans> 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<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
@ -1644,10 +1694,12 @@ NetlistBrowserPage::update_highlights ()
}
}
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
size_t n_markers = 0;
bool not_all_shapes_are_shown = false;
for (std::vector<lay::NetlistObjectsPath>::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<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
for (std::vector<db::DCplxTrans>::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<bool, db::DCplxTrans> 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 ("<html><p style=\"color:red; font-weight: bold\">" +
tl::to_string (QObject::tr ("Not all shapes are highlighted")) +

View File

@ -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<lay::NetlistObjectsPath> m_selected_paths;
std::vector<std::pair<const db::Circuit *, db::DPolygon> > m_markers;
lay::NetInfoDialog *mp_info_dialog;
tl::DeferredMethod<NetlistBrowserPage> dm_update_highlights;
tl::DeferredMethod<NetlistBrowserPage> dm_rerun_macro;

View File

@ -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<const db::Circuit *, const db::Circuit *> &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<const db::Circuit *, const db::Circuit *> &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);
}
}

View File

@ -27,8 +27,10 @@
#include "layuiCommon.h"
#include "dbNetlistCrossReference.h"
#include "dbLayoutToNetlist.h"
#include <QAbstractItemModel>
#include <QIcon>
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<std::pair<const db::Circuit *, const db::Circuit *>, const db::NetlistCrossReference::PerCircuitData::log_entries_type *> circuit_entry;
std::vector<circuit_entry> 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;
};
}

View File

@ -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");
}

View File

@ -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

View File

@ -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")

Binary file not shown.

Binary file not shown.

Binary file not shown.

19
testdata/algo/l2n_reader_6.l2n vendored Normal file
View File

@ -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.

7
testdata/algo/l2n_reader_6s.l2n vendored Normal file
View File

@ -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))

19
testdata/algo/l2n_reader_au_6.l2n vendored Normal file
View File

@ -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.

18
testdata/algo/l2n_writer_au_3.txt vendored Normal file
View File

@ -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.

6
testdata/algo/l2n_writer_au_3s.txt vendored Normal file
View File

@ -0,0 +1,6 @@
#%l2n-klayout
W(TOP)
U(0.001)
H(I B(info))
H(W B(warning))
H(E B(error))

View File

@ -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
)

View File

@ -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(
)
)

View File

@ -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

View File

@ -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

Binary file not shown.

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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")

View File

@ -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

23
testdata/lvs/must_connect1.cir vendored Normal file
View File

@ -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

BIN
testdata/lvs/must_connect1.gds vendored Normal file

Binary file not shown.

139
testdata/lvs/must_connect1.lvs vendored Normal file
View File

@ -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

484
testdata/lvs/must_connect1.lvsdb vendored Normal file
View File

@ -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)
)
)
)

24
testdata/lvs/must_connect1.sch vendored Normal file
View File

@ -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

23
testdata/lvs/must_connect1_tl.cir vendored Normal file
View File

@ -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

143
testdata/lvs/must_connect1_tl.lvs vendored Normal file
View File

@ -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

484
testdata/lvs/must_connect1_tl.lvsdb vendored Normal file
View File

@ -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)
)
)
)

23
testdata/lvs/must_connect2.cir vendored Normal file
View File

@ -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

BIN
testdata/lvs/must_connect2.gds vendored Normal file

Binary file not shown.

142
testdata/lvs/must_connect2.lvs vendored Normal file
View File

@ -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

490
testdata/lvs/must_connect2.lvsdb vendored Normal file
View File

@ -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)
)
)
)

24
testdata/lvs/must_connect2.sch vendored Normal file
View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

Some files were not shown because too many files have changed in this diff Show More