mirror of https://github.com/KLayout/klayout.git
commit
8d51124fd4
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace db
|
|||
{
|
||||
|
||||
NetlistCrossReference::NetlistCrossReference ()
|
||||
: mp_per_circuit_data (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri)
|
|||
SOURCES = \
|
||||
dbCompoundOperationTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
dbLogTests.cc \
|
||||
dbRecursiveInstanceIteratorTests.cc \
|
||||
dbRegionCheckUtilsTests.cc \
|
||||
dbUtilsTests.cc \
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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")) +
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
@ -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))
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#%l2n-klayout
|
||||
W(TOP)
|
||||
U(0.001)
|
||||
H(I B(info))
|
||||
H(W B(warning))
|
||||
H(E B(error))
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Binary file not shown.
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -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
|
||||
Binary file not shown.
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue