diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index ab0a8e079..eaea268ae 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -69,7 +69,7 @@ namespace db * A net declaration shall be there also if no geometry * is present. The ID is a numerical shortcut for the net. * pin( ) - outgoing pin connection [short key: P] - * device( [combined-device]* [terminal-route]* [device-def]) + * device( [combined-device]* [terminal-route]* [device-def]) * - device with connections [short key: D] * circuit( [subcircuit-def]) * - subcircuit with connections [short key: X] diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index b78e0d010..48ef7171e 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -335,10 +335,8 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo dm->set_name (name); netlist->add_device_abstract (dm); - if (l2n) { - db::cell_index_type ci = l2n->internal_layout ()->add_cell (name.c_str ()); - dm->set_cell_index (ci); - } + db::cell_index_type ci = l2n->internal_layout ()->add_cell (name.c_str ()); + dm->set_cell_index (ci); std::string cls; read_word_or_quoted (cls); @@ -522,16 +520,21 @@ LayoutToNetlistStandardReader::terminal_id (const db::DeviceClass *device_class, throw tl::Exception (tl::to_string (tr ("Not a valid terminal name: ")) + tname + tl::to_string (tr (" for device class: ")) + device_class->name ()); } -db::DeviceAbstract * +std::pair LayoutToNetlistStandardReader::device_model_by_name (db::Netlist *netlist, const std::string &dmname) { for (db::Netlist::device_abstract_iterator i = netlist->begin_device_abstracts (); i != netlist->end_device_abstracts (); ++i) { if (i->name () == dmname) { - return i.operator-> (); + return std::make_pair (i.operator-> (), i->device_class ()); } } - throw tl::Exception (tl::to_string (tr ("Not a valid device abstract name: ")) + dmname); + db::DeviceClass *cls = netlist->device_class_by_name (dmname); + if (! cls) { + throw tl::Exception (tl::to_string (tr ("Not a valid device abstract name: ")) + dmname); + } + + return std::make_pair ((db::DeviceAbstract *) 0, cls); } void @@ -545,11 +548,11 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe std::string dmname; read_word_or_quoted (dmname); - db::DeviceAbstract *dm = device_model_by_name (netlist, dmname); + std::pair dm = device_model_by_name (netlist, dmname); db::Device *device = new db::Device (); - device->set_device_class (const_cast (dm->device_class ())); - device->set_device_abstract (dm); + device->set_device_class (const_cast (dm.second)); + device->set_device_abstract (dm.first); device->set_name (name); circuit->add_device (device); @@ -581,7 +584,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe br2.done (); - db::DeviceAbstract *da = device_model_by_name (netlist, n); + db::DeviceAbstract *da = device_model_by_name (netlist, n).first; device->other_abstracts ().push_back (db::DeviceAbstractRef (da, db::DVector (dbu * dx, dbu * dy))); @@ -601,8 +604,8 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe throw tl::Exception (tl::to_string (tr ("Not a valid device component index: ")) + tl::to_string (device_comp_index)); } - size_t touter_id = terminal_id (dm->device_class (), touter); - size_t tinner_id = terminal_id (dm->device_class (), tinner); + size_t touter_id = terminal_id (dm.second, touter); + size_t tinner_id = terminal_id (dm.second, tinner); device->reconnected_terminals () [touter_id].push_back (db::DeviceReconnectedTerminal (size_t (device_comp_index), tinner_id)); @@ -614,7 +617,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe unsigned int netid = (unsigned int) read_int (); br2.done (); - size_t tid = terminal_id (dm->device_class (), tname); + size_t tid = terminal_id (dm.second, tname); max_tid = std::max (max_tid, tid + 1); db::Net *net = id2net [netid]; @@ -633,7 +636,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe br2.done (); size_t pid = std::numeric_limits::max (); - const std::vector &pd = dm->device_class ()->parameter_definitions (); + const std::vector &pd = dm.second->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { if (p->name () == pname) { pid = p->id (); @@ -644,7 +647,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe // if no parameter with this name exists, create one if (pid == std::numeric_limits::max ()) { // TODO: this should only happen for generic devices - db::DeviceClass *dc = const_cast (dm->device_class ()); + db::DeviceClass *dc = const_cast (dm.second); pid = dc->add_parameter_definition (db::DeviceParameterDefinition (pname, std::string ())).id (); } @@ -660,14 +663,14 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe br.done (); - if (l2n) { + if (l2n && dm.first) { db::Cell &ccell = l2n->internal_layout ()->cell (circuit->cell_index ()); // make device cell instances std::vector insts; - db::CellInstArray inst (db::CellInst (dm->cell_index ()), db::Trans (db::Vector (x, y))); + db::CellInstArray inst (db::CellInst (dm.first->cell_index ()), db::Trans (db::Vector (x, y))); ccell.insert (inst); insts.push_back (inst); @@ -695,7 +698,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe if (tr) { for (std::vector::const_iterator i = tr->begin (); i != tr->end (); ++i) { - const db::DeviceAbstract *da = dm; + const db::DeviceAbstract *da = dm.first; if (i->device_index > 0) { da = device->other_abstracts () [i->device_index - 1].device_abstract; } @@ -707,7 +710,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe } else { - Connections ref (net->cluster_id (), dm->cluster_id_for_terminal (tid)); + Connections ref (net->cluster_id (), dm.first->cluster_id_for_terminal (tid)); connections [insts [0]].push_back (ref); } diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index 87f5e5413..51e000b07 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -94,7 +94,7 @@ protected: void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, bool nested = false, std::map > *id2net_per_circuit = 0); static size_t terminal_id (const db::DeviceClass *device_class, const std::string &tname); - static db::DeviceAbstract *device_model_by_name (db::Netlist *netlist, const std::string &dmname); + static std::pair device_model_by_name (db::Netlist *netlist, const std::string &dmname); tl::TextInputStream &stream (); const std::string &path () const; diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index ac52f6ee2..676c6bc21 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -50,16 +50,13 @@ void LayoutToNetlistWriterBase::write (const db::LayoutToNetlist *l2n) namespace l2n_std_format { -template class std_writer_impl >; -template class std_writer_impl >; - static const std::string endl ("\n"); static const std::string indent1 (" "); static const std::string indent2 (" "); template -std_writer_impl::std_writer_impl (tl::OutputStream &stream) - : mp_stream (&stream) +std_writer_impl::std_writer_impl (tl::OutputStream &stream, double dbu) + : mp_stream (&stream), m_dbu (dbu) { // .. nothing yet .. } @@ -77,7 +74,6 @@ template void std_writer_impl::write (const db::LayoutToNetlist *l2n) { write (l2n->netlist (), l2n, false, 0); - } template @@ -103,15 +99,16 @@ void std_writer_impl::write (const db::Netlist *nl, const db::LayoutToNetl } if (ly) { *mp_stream << indent << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl; - *mp_stream << indent << Keys::unit_key << "(" << ly->dbu () << ")" << endl; + *mp_stream << indent << Keys::unit_key << "(" << m_dbu << ")" << endl; } - if (! Keys::is_short ()) { - *mp_stream << endl << indent << "# Layer section" << endl; - *mp_stream << indent << "# This section lists the mask layers (drawing or derived) and their connections." << endl; - } + if (l2n) { + + if (! Keys::is_short ()) { + *mp_stream << endl << indent << "# Layer section" << endl; + *mp_stream << indent << "# This section lists the mask layers (drawing or derived) and their connections." << endl; + } - if (ly) { if (! Keys::is_short ()) { *mp_stream << endl << indent << "# Mask layers" << endl; } @@ -123,9 +120,6 @@ void std_writer_impl::write (const db::Netlist *nl, const db::LayoutToNetl } *mp_stream << indent << ")" << endl; } - } - - if (l2n) { if (! Keys::is_short ()) { *mp_stream << endl << indent << "# Mask layer connectivity" << endl; @@ -190,19 +184,19 @@ void std_writer_impl::write (const db::Netlist *nl, const db::LayoutToNetl } if (! Keys::is_short ()) { - *mp_stream << endl << "# Circuit section" << endl; - *mp_stream << "# Circuits are the hierarchical building blocks of the netlist." << endl; + *mp_stream << endl << indent << "# Circuit section" << endl; + *mp_stream << indent << "# Circuits are the hierarchical building blocks of the netlist." << endl; } for (db::Netlist::const_bottom_up_circuit_iterator i = nl->begin_bottom_up (); i != nl->end_bottom_up (); ++i) { const db::Circuit *x = *i; *mp_stream << indent << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl; - write (l2n, *x, indent, net2id_per_circuit); + write (nl, l2n, *x, indent, net2id_per_circuit); *mp_stream << indent << ")" << endl; } } template -void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit, const std::string &indent, std::map > *net2id_per_circuit) +void std_writer_impl::write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Circuit &circuit, const std::string &indent, std::map > *net2id_per_circuit) { std::map net2id_local; std::map *net2id = &net2id_local; @@ -211,14 +205,16 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Cir } unsigned int id = 0; + for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { + net2id->insert (std::make_pair (n.operator-> (), ++id)); + } - if (circuit.begin_nets () != circuit.end_nets ()) { + if (l2n && circuit.begin_nets () != circuit.end_nets ()) { if (! Keys::is_short ()) { *mp_stream << endl << indent << indent1 << "# Nets with their geometries" << endl; } for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) { - net2id->insert (std::make_pair (n.operator-> (), ++id)); - write (l2n, *n, id, indent); + write (netlist, l2n, *n, (*net2id) [n.operator-> ()], indent); } } @@ -336,12 +332,8 @@ void std_writer_impl::write (const db::PolygonRef *s, const db::ICplxTrans } template -void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id, const std::string &indent) +void std_writer_impl::write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id, const std::string &indent) { - if (! l2n->netlist ()) { - throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before extraction has been done"))); - } - const db::hier_clusters &clusters = l2n->net_clusters (); const db::Circuit *circuit = net.circuit (); const db::Connectivity &conn = l2n->connectivity (); @@ -361,7 +353,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net // vanish in "purge" but the clusters will still be there we need to recursive into clusters from // unknown cells. db::cell_index_type ci = si.cell_index (); - if (ci != prev_ci && ci != cci && (l2n->netlist ()->circuit_by_cell_index (ci) || l2n->netlist ()->device_abstract_by_cell_index (ci))) { + if (ci != prev_ci && ci != cci && (netlist->circuit_by_cell_index (ci) || netlist->device_abstract_by_cell_index (ci))) { si.skip_cell (); @@ -376,7 +368,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net any = true; } - *mp_stream << indent2; + *mp_stream << indent << indent2; write (si.operator-> (), si.trans (), name_for_layer (l2n, *l), true); *mp_stream << endl; @@ -394,7 +386,7 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net *mp_stream << indent << indent1 << ")" << endl; } else { - *mp_stream << indent1 << Keys::net_key << "(" << id; + *mp_stream << indent << indent1 << Keys::net_key << "(" << id; if (! net.name ().empty ()) { *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; } @@ -406,23 +398,24 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Net template void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map &net2id, const std::string &indent) { - const db::Layout *ly = l2n->internal_layout (); - double dbu = ly->dbu (); - *mp_stream << indent << indent1 << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ()); *mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ()); - const db::DCplxTrans &tr = subcircuit.trans (); - if (tr.is_mag ()) { - *mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")"; + if (l2n) { + + const db::DCplxTrans &tr = subcircuit.trans (); + if (tr.is_mag ()) { + *mp_stream << " " << Keys::scale_key << "(" << tr.mag () << ")"; + } + if (tr.is_mirror ()) { + *mp_stream << " " << Keys::mirror_key; + } + if (fabs (tr.angle ()) > 1e-6) { + *mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")"; + } + *mp_stream << " " << Keys::location_key << "(" << tr.disp ().x () / m_dbu << " " << tr.disp ().y () / m_dbu << ")"; + } - if (tr.is_mirror ()) { - *mp_stream << " " << Keys::mirror_key; - } - if (fabs (tr.angle ()) > 1e-6) { - *mp_stream << " " << Keys::rotation_key << "(" << tr.angle () << ")"; - } - *mp_stream << " " << Keys::location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")"; // each pin in one line for more than a few pins bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1); @@ -486,11 +479,9 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev } template -void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map &net2id, const std::string &indent) +void std_writer_impl::write (const db::LayoutToNetlist * /*l2n*/, const db::Device &device, std::map &net2id, const std::string &indent) { - const db::Layout *ly = l2n->internal_layout (); - double dbu = ly->dbu (); - db::VCplxTrans dbu_inv (1.0 / dbu); + db::VCplxTrans dbu_inv (1.0 / m_dbu); tl_assert (device.device_class () != 0); const std::vector &td = device.device_class ()->terminal_definitions (); @@ -498,31 +489,35 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev *mp_stream << indent << indent1 << Keys::device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ()); - tl_assert (device.device_abstract () != 0); - *mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl; + if (device.device_abstract ()) { - const std::vector &other_abstracts = device.other_abstracts (); - for (std::vector::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) { + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl; - db::Vector pos = dbu_inv * a->offset; + const std::vector &other_abstracts = device.other_abstracts (); + for (std::vector::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) { - *mp_stream << indent << indent2 << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " " << pos.x () << " " << pos.y () << ")" << endl; + db::Vector pos = dbu_inv * a->offset; - } + *mp_stream << indent << indent2 << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " " << pos.x () << " " << pos.y () << ")" << endl; - const std::map > &reconnected_terminals = device.reconnected_terminals (); - for (std::map >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) { - - for (std::vector::const_iterator c = t->second.begin (); c != t->second.end (); ++c) { - *mp_stream << indent << indent2 << Keys::connect_key << "(" << c->device_index << " " << tl::to_word_or_quoted_string (td [t->first].name ()) << " " << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()) << ")" << endl; } + const std::map > &reconnected_terminals = device.reconnected_terminals (); + for (std::map >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) { + + for (std::vector::const_iterator c = t->second.begin (); c != t->second.end (); ++c) { + *mp_stream << indent << indent2 << Keys::connect_key << "(" << c->device_index << " " << tl::to_word_or_quoted_string (td [t->first].name ()) << " " << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()) << ")" << endl; + } + + } + + db::Point pos = dbu_inv * device.position (); + *mp_stream << indent << indent2 << Keys::location_key << "(" << pos.x () << " " << pos.y () << ")" << endl; + + } else { + *mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl; } - db::Point pos = dbu_inv * device.position (); - - *mp_stream << indent << indent2 << Keys::location_key << "(" << pos.x () << " " << pos.y () << ")" << endl; - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { *mp_stream << indent << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl; } @@ -537,6 +532,10 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Dev *mp_stream << indent << indent1 << ")" << endl; } +// explicit instantiation +template class std_writer_impl >; +template class std_writer_impl >; + } // ------------------------------------------------------------------------------------------- @@ -550,11 +549,20 @@ LayoutToNetlistStandardWriter::LayoutToNetlistStandardWriter (tl::OutputStream & void LayoutToNetlistStandardWriter::do_write (const db::LayoutToNetlist *l2n) { + if (! l2n->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before the netlist has been created"))); + } + if (! l2n->internal_layout ()) { + throw tl::Exception (tl::to_string (tr ("Can't write annotated netlist before the layout has been loaded"))); + } + + double dbu = l2n->internal_layout ()->dbu (); + if (m_short_version) { - l2n_std_format::std_writer_impl > writer (*mp_stream); + l2n_std_format::std_writer_impl > writer (*mp_stream, dbu); writer.write (l2n); } else { - l2n_std_format::std_writer_impl > writer (*mp_stream); + l2n_std_format::std_writer_impl > writer (*mp_stream, dbu); writer.write (l2n); } } diff --git a/src/db/db/dbLayoutToNetlistWriter.h b/src/db/db/dbLayoutToNetlistWriter.h index 7bb1ed4ca..a7879916f 100644 --- a/src/db/db/dbLayoutToNetlistWriter.h +++ b/src/db/db/dbLayoutToNetlistWriter.h @@ -47,14 +47,14 @@ template class std_writer_impl { public: - std_writer_impl (tl::OutputStream &stream); + std_writer_impl (tl::OutputStream &stream, double dbu); void write (const db::LayoutToNetlist *l2n); protected: void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map > *net2id_per_circuit); - void write (const db::LayoutToNetlist *l2n, const db::Circuit &circuit, const std::string &indent, std::map > *net2id_per_circuit); - void write (const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id, const std::string &indent); + void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Circuit &circuit, const std::string &indent, std::map > *net2id_per_circuit); + void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, const db::Net &net, unsigned int id, const std::string &indent); void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit, std::map &net2id, const std::string &indent); void write (const db::LayoutToNetlist *l2n, const db::Device &device, std::map &net2id, const std::string &indent); void write (const db::LayoutToNetlist *l2n, const db::DeviceAbstract &device_abstract, const std::string &indent); @@ -69,6 +69,7 @@ protected: private: tl::OutputStream *mp_stream; db::Point m_ref; + double m_dbu; }; } diff --git a/src/db/db/dbLayoutVsSchematicFormatDefs.h b/src/db/db/dbLayoutVsSchematicFormatDefs.h index de47bc1ba..92554532e 100644 --- a/src/db/db/dbLayoutVsSchematicFormatDefs.h +++ b/src/db/db/dbLayoutVsSchematicFormatDefs.h @@ -120,6 +120,8 @@ namespace lvs_std_format struct DB_PUBLIC keys : public l2n_std_format::keys { + typedef l2n_std_format::keys l2n_keys; + static const std::string reference_key; static const std::string layout_key; static const std::string xref_key; diff --git a/src/db/db/dbLayoutVsSchematicWriter.cc b/src/db/db/dbLayoutVsSchematicWriter.cc index 43233bd7a..79379c35b 100644 --- a/src/db/db/dbLayoutVsSchematicWriter.cc +++ b/src/db/db/dbLayoutVsSchematicWriter.cc @@ -56,17 +56,17 @@ namespace lvs_std_format template class std_writer_impl - : public l2n_std_format::std_writer_impl + : public l2n_std_format::std_writer_impl { public: - std_writer_impl (tl::OutputStream &stream); + std_writer_impl (tl::OutputStream &stream, double dbu); void write (const db::LayoutVsSchematic *l2n); private: tl::OutputStream &stream () { - return l2n_std_format::std_writer_impl::stream (); + return l2n_std_format::std_writer_impl::stream (); } std::string status_to_s (const db::NetlistCrossReference::Status status); @@ -80,8 +80,8 @@ static const std::string indent1 (" "); static const std::string indent2 (" "); template -std_writer_impl::std_writer_impl (tl::OutputStream &stream) - : l2n_std_format::std_writer_impl (stream) +std_writer_impl::std_writer_impl (tl::OutputStream &stream, double dbu) + : l2n_std_format::std_writer_impl (stream, dbu) { // .. nothing yet .. } @@ -101,26 +101,32 @@ void std_writer_impl::write (const db::LayoutVsSchematic *lvs) stream () << Keys::version_key << "(" << version << ")" << endl; } - if (! Keys::is_short ()) { - stream () << endl << "# Layout" << endl; + if (lvs->netlist ()) { + if (! Keys::is_short ()) { + stream () << endl << "# Layout" << endl; + } + stream () << Keys::layout_key << "(" << endl; + l2n_std_format::std_writer_impl::write (lvs->netlist (), lvs, true, &m_net2id_per_circuit_a); + stream () << ")" << endl; } - stream () << Keys::layout_key << "(" << endl; - l2n_std_format::std_writer_impl::write (0, lvs, true, &m_net2id_per_circuit_a); - stream () << ")" << endl; - if (! Keys::is_short ()) { - stream () << endl << "# Reference netlist" << endl; + if (lvs->reference_netlist ()) { + if (! Keys::is_short ()) { + stream () << endl << "# Reference netlist" << endl; + } + stream () << Keys::reference_key << "(" << endl; + l2n_std_format::std_writer_impl::write (lvs->reference_netlist (), 0, true, &m_net2id_per_circuit_b); + stream () << ")" << endl; } - stream () << Keys::reference_key << "(" << endl; - l2n_std_format::std_writer_impl::write (lvs->reference_netlist (), 0, true, &m_net2id_per_circuit_b); - stream () << ")" << endl; - if (! Keys::is_short ()) { - stream () << endl << "# Cross reference" << endl; + if (lvs->cross_ref ()) { + if (! Keys::is_short ()) { + stream () << endl << "# Cross reference" << endl; + } + stream () << Keys::xref_key << "(" << endl; + write (lvs->cross_ref ()); + stream () << ")" << endl; } - stream () << Keys::xref_key << "(" << endl; - write (lvs->cross_ref ()); - stream () << ")" << endl; if (! Keys::is_short ()) { stream () << endl; @@ -222,11 +228,20 @@ LayoutVsSchematicStandardWriter::LayoutVsSchematicStandardWriter (tl::OutputStre void LayoutVsSchematicStandardWriter::do_write_lvs (const db::LayoutVsSchematic *lvs) { + if (! lvs->netlist ()) { + throw tl::Exception (tl::to_string (tr ("Can't write LVS DB before the netlist has been created"))); + } + if (! lvs->internal_layout ()) { + throw tl::Exception (tl::to_string (tr ("Can't write LVS DB before the layout has been loaded"))); + } + + double dbu = lvs->internal_layout ()->dbu (); + if (m_short_version) { - lvs_std_format::std_writer_impl > writer (*mp_stream); + lvs_std_format::std_writer_impl > writer (*mp_stream, dbu); writer.write (lvs); } else { - lvs_std_format::std_writer_impl > writer (*mp_stream); + lvs_std_format::std_writer_impl > writer (*mp_stream, dbu); writer.write (lvs); } } diff --git a/src/db/unit_tests/dbLayoutVsSchematicTests.cc b/src/db/unit_tests/dbLayoutVsSchematicTests.cc new file mode 100644 index 000000000..e941bfffa --- /dev/null +++ b/src/db/unit_tests/dbLayoutVsSchematicTests.cc @@ -0,0 +1,231 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbNetlistDeviceExtractorClasses.h" +#include "dbLayoutVsSchematic.h" +#include "dbStream.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" +#include "dbTestSupport.h" +#include "dbNetlistSpiceWriter.h" // to create debug files +#include "dbNetlistSpiceReader.h" +#include "dbNetlistCompare.h" + +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlFileUtils.h" + +#include +#include + +static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_layer, int gds_datatype = 0) +{ + unsigned int lid = ly.insert_layer (db::LayerProperties (gds_layer, gds_datatype)); + lmap.map (ly.get_properties (lid), lid); + return lid; +} + +TEST(1_BasicFlow) +{ + 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); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "lvs_test_1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutVsSchematic lvs (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rbulk (lvs.make_layer ("bulk")); + std::auto_ptr rnwell (lvs.make_layer (nwell, "nwell")); + std::auto_ptr ractive (lvs.make_layer (active, "active")); + std::auto_ptr rpplus (lvs.make_layer (pplus, "pplus")); + std::auto_ptr rnplus (lvs.make_layer (nplus, "nplus")); + std::auto_ptr rpoly (lvs.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (lvs.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (lvs.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (lvs.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (lvs.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (lvs.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (lvs.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (lvs.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (lvs.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 (); + lvs.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 (); + lvs.extract_devices (nmos_ex, dl); + + // net extraction + + lvs.register_layer (rpsd, "psd"); + lvs.register_layer (rnsd, "nsd"); + lvs.register_layer (rptie, "ptie"); + lvs.register_layer (rntie, "ntie"); + + // Intra-layer + lvs.connect (rpsd); + lvs.connect (rnsd); + lvs.connect (*rnwell); + lvs.connect (*rpoly); + lvs.connect (*rdiff_cont); + lvs.connect (*rpoly_cont); + lvs.connect (*rmetal1); + lvs.connect (*rvia1); + lvs.connect (*rmetal2); + lvs.connect (rptie); + lvs.connect (rntie); + // Inter-layer + lvs.connect (rpsd, *rdiff_cont); + lvs.connect (rnsd, *rdiff_cont); + lvs.connect (*rpoly, *rpoly_cont); + lvs.connect (*rpoly_cont, *rmetal1); + lvs.connect (*rdiff_cont, *rmetal1); + lvs.connect (*rdiff_cont, rptie); + lvs.connect (*rdiff_cont, rntie); + lvs.connect (*rnwell, rntie); + lvs.connect (*rmetal1, *rvia1); + lvs.connect (*rvia1, *rmetal2); + lvs.connect (*rpoly, *rpoly_lbl); // attaches labels + lvs.connect (*rmetal1, *rmetal1_lbl); // attaches labels + lvs.connect (*rmetal2, *rmetal2_lbl); // attaches labels + // Global + lvs.connect_global (rptie, "BULK"); + lvs.connect_global (*rbulk, "BULK"); + + // 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); + + lvs.extract_netlist (); + + // doesn't do anything here, but we test that this does not destroy anything: + lvs.netlist ()->combine_devices (); + + // make pins for named nets of top-level circuits - this way they are not purged + lvs.netlist ()->make_top_level_pins (); + lvs.netlist ()->purge (); + + // read the reference netlist + { + db::NetlistSpiceReader reader; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "lvs_test_1.spi"); + + std::auto_ptr netlist (new db::Netlist ()); + tl::InputStream stream (fn); + reader.read (stream, *netlist); + lvs.set_reference_netlist (netlist.release ()); + } + + // perform the compare + { + db::NetlistComparer comparer; + lvs.compare_netlists (&comparer); + } + + // produce the output + { + // @@@ + lvs.save ("lvs_test_1.lvsdb", false); + } +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 39a1196b1..f3e14367f 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -71,7 +71,8 @@ SOURCES = \ dbDeepEdgesTests.cc \ dbDeepEdgePairsTests.cc \ dbNetlistCompareTests.cc \ - dbNetlistReaderTests.cc + dbNetlistReaderTests.cc \ + dbLayoutVsSchematicTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC