diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 67ee164c9..1ca2d79c7 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -292,6 +292,13 @@ void Circuit::clear_pins () m_pins.clear (); } +const Pin &Circuit::add_pin (const Pin &pin) +{ + m_pins.push_back (pin); + m_pins.back ().set_id (m_pins.size () - 1); + return m_pins.back (); +} + const Pin &Circuit::add_pin (const std::string &name) { m_pins.push_back (Pin (name)); diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 38f66b17b..c8bcac695 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -324,6 +324,12 @@ public: */ const Pin &add_pin (const std::string &name); + /** + * @brief Adds a pin to this circuit + * This version uses the given pin as the template. + */ + const Pin &add_pin (const Pin &pin); + /** * @brief Begin iterator for the pins of the circuit (non-const version) */ diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.cc b/src/db/db/dbLayoutToNetlistFormatDefs.cc index eabcaa83a..ce6532619 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.cc +++ b/src/db/db/dbLayoutToNetlistFormatDefs.cc @@ -43,6 +43,7 @@ namespace l2n_std_format DB_PUBLIC std::string LongKeys::circuit_key ("circuit"); DB_PUBLIC std::string LongKeys::net_key ("net"); DB_PUBLIC std::string LongKeys::name_key ("name"); + DB_PUBLIC std::string LongKeys::property_key ("property"); DB_PUBLIC std::string LongKeys::device_key ("device"); DB_PUBLIC std::string LongKeys::polygon_key ("polygon"); DB_PUBLIC std::string LongKeys::rect_key ("rect"); @@ -55,7 +56,7 @@ namespace l2n_std_format DB_PUBLIC std::string LongKeys::scale_key ("scale"); DB_PUBLIC std::string LongKeys::pin_key ("pin"); - // A, B, C, D, E, G, I, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y + // A, B, C, D, E, F, G, I, 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"); @@ -67,6 +68,7 @@ namespace l2n_std_format DB_PUBLIC std::string ShortKeys::circuit_key ("X"); DB_PUBLIC std::string ShortKeys::net_key ("N"); DB_PUBLIC std::string ShortKeys::name_key ("I"); + DB_PUBLIC std::string ShortKeys::property_key ("F"); DB_PUBLIC std::string ShortKeys::device_key ("D"); DB_PUBLIC std::string ShortKeys::polygon_key ("Q"); DB_PUBLIC std::string ShortKeys::rect_key ("R"); diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index 8d70abcbc..f0b595290 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -66,7 +66,9 @@ namespace db * * [boundary-def] * - * net( [name]? [geometry-def]*) + * [property-def]* + * + * net( [name]? [property-def]* [geometry-def]*) * - net geometry [short key: N] * A net declaration shall be there also if no geometry * is present. The ID is a numerical shortcut for the net. @@ -103,6 +105,14 @@ namespace db * * name() - specify net name [short key: I] * + * [property-def]: + * + * property( ) + * - specifies a property value/key pair [short key: F] + * prop-name and prop-value are variant specifications + * in klayout notation: #x is an integer, ##y a floating-point + * value, a word or quoted literal is a string. + * * [geometry-def]: * * polygon( [coord] ...) - defines a polygon [short key: Q] @@ -123,24 +133,26 @@ namespace db * * [device-def]: * - * [trans-def] - location of the device [short key Y] + * [property-def]* - user properties + * [trans-def] - location of the device * must be before terminal - * param( ) - defines a parameter [short key E] + * param( ) - defines a parameter [short key: E] * terminal( ) * - specifies connection of the terminal with * a net (short key: T) * * [subcircuit-def]: * - * [trans-def] - location of the subcircuit [short key Y] + * [property-def]* - user properties + * [trans-def] - location of the subcircuit * pin( ) - specifies connection of the pin with a net [short key: P] * * [trans-def]: * - * location( ) - location of the instance [short key Y] - * rotation() - rotation angle (in degree, default is 0) [short key O] - * mirror - if specified, the instance is mirrored before rotation [short key M] - * scale() - magnification (default is 1) [short key S] + * location( ) - location of the instance [short key: Y] + * rotation() - rotation angle (in degree, default is 0) [short key: O] + * mirror - if specified, the instance is mirrored before rotation [short key: M] + * scale() - magnification (default is 1) [short key: S] */ namespace l2n_std_format @@ -160,6 +172,7 @@ namespace l2n_std_format static std::string circuit_key; static std::string net_key; static std::string name_key; + static std::string property_key; static std::string device_key; static std::string subcircuit_key; static std::string polygon_key; @@ -191,6 +204,7 @@ namespace l2n_std_format static std::string circuit_key; static std::string net_key; static std::string name_key; + static std::string property_key; static std::string device_key; static std::string subcircuit_key; static std::string polygon_key; diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index 5cfa6ac49..0c073aaf6 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -305,7 +305,9 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo while (br) { - if (test (skeys::rect_key) || test (lkeys::rect_key)) { + if (test (skeys::property_key) || test (lkeys::property_key)) { + read_property (circuit); + } else if (test (skeys::rect_key) || test (lkeys::rect_key)) { circuit->set_boundary (db::DPolygon (dbu * read_rect ())); } else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) { circuit->set_boundary (read_polygon ().transformed (dbu)); @@ -423,6 +425,22 @@ LayoutToNetlistStandardReader::read_point () return m_ref; } +void +LayoutToNetlistStandardReader::read_property (db::NetlistObject *obj) +{ + Brace br (this); + + tl::Variant k, v; + m_ex.read (k); + m_ex.read (v); + + if (obj) { + obj->set_property (k, v); + } + + br.done (); +} + std::pair LayoutToNetlistStandardReader::read_geometry (db::LayoutToNetlist *l2n) { @@ -502,14 +520,18 @@ LayoutToNetlistStandardReader::read_polygon () } void -LayoutToNetlistStandardReader::read_geometries (Brace &br, db::LayoutToNetlist *l2n, db::local_cluster &lc, db::Cell &cell) +LayoutToNetlistStandardReader::read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster &lc, db::Cell &cell) { m_ref = db::Point (); while (br) { - std::pair pr = read_geometry (l2n); - lc.add (pr.second, pr.first); - cell.shapes (pr.first).insert (pr.second); + if (test (skeys::property_key) || test (lkeys::property_key)) { + read_property (obj); + } else { + std::pair pr = read_geometry (l2n); + lc.add (pr.second, pr.first); + cell.shapes (pr.first).insert (pr.second); + } } } @@ -540,7 +562,7 @@ LayoutToNetlistStandardReader::read_net (db::Netlist * /*netlist*/, db::LayoutTo net->set_cluster_id (lc.id ()); db::Cell &cell = l2n->internal_layout ()->cell (circuit->cell_index ()); - read_geometries (br, l2n, lc, cell); + read_geometries (net, br, l2n, lc, cell); } @@ -552,21 +574,28 @@ LayoutToNetlistStandardReader::read_pin (db::Netlist * /*netlist*/, db::LayoutTo { Brace br (this); - std::string name; db::Net *net = 0; + db::Pin pin; + while (br) { if (test (skeys::name_key) || test (lkeys::name_key)) { - if (!name.empty ()) { + if (! pin.name ().empty ()) { throw tl::Exception (tl::to_string (tr ("Duplicate pin name"))); } Brace br_name (this); - read_word_or_quoted (name); + std::string n; + read_word_or_quoted (n); + pin.set_name (n); br_name.done (); + } else if (test (skeys::property_key) || test (lkeys::property_key)) { + + read_property (&pin); + } else { if (net) { @@ -583,9 +612,9 @@ LayoutToNetlistStandardReader::read_pin (db::Netlist * /*netlist*/, db::LayoutTo } - const db::Pin &pin = circuit->add_pin (name); + size_t pin_id = circuit->add_pin (pin).id (); if (net) { - circuit->connect_pin (pin.id (), net); + circuit->connect_pin (pin_id, net); } br.done (); @@ -657,6 +686,10 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe // .. nothing yet .. + } else if (test (skeys::property_key) || test (lkeys::property_key)) { + + read_property (device.get ()); + } else if (test (skeys::device_key) || test (lkeys::device_key)) { std::string n; @@ -900,6 +933,10 @@ LayoutToNetlistStandardReader::read_subcircuit (db::Netlist *netlist, db::Layout // .. nothing yet .. + } else if (test (skeys::property_key) || test (lkeys::property_key)) { + + read_property (subcircuit.get ()); + } else if (test (skeys::pin_key) || test (lkeys::pin_key)) { Brace br2 (this); @@ -989,7 +1026,7 @@ LayoutToNetlistStandardReader::read_abstract_terminal (db::LayoutToNetlist *l2n, dm->set_cluster_id_for_terminal (tid, lc.id ()); db::Cell &cell = l2n->internal_layout ()->cell (dm->cell_index ()); - read_geometries (br, l2n, lc, cell); + read_geometries (0, br, l2n, lc, cell); } diff --git a/src/db/db/dbLayoutToNetlistReader.h b/src/db/db/dbLayoutToNetlistReader.h index 5d256c84e..db2c9d665 100644 --- a/src/db/db/dbLayoutToNetlistReader.h +++ b/src/db/db/dbLayoutToNetlistReader.h @@ -140,9 +140,10 @@ protected: bool read_trans_part (db::DCplxTrans &tr); void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc); std::pair read_geometry (db::LayoutToNetlist *l2n); + void read_property (db::NetlistObject *obj); db::Polygon read_polygon (); db::Box read_rect (); - void read_geometries (Brace &br, db::LayoutToNetlist *l2n, db::local_cluster &lc, db::Cell &cell); + void read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster &lc, db::Cell &cell); db::Point read_point (); private: diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index b36eaf9ba..fad0c3a9f 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -278,6 +278,13 @@ void std_writer_impl::write (const db::Netlist *netlist, const db::LayoutT } + for (db::NetlistObject::property_iterator p = circuit.begin_properties (); p != circuit.end_properties (); ++p) { + if (p == circuit.begin_properties() && ! Keys::is_short ()) { + *mp_stream << endl << indent << indent1 << "# Properties" << endl; + } + *mp_stream << indent << indent1 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + } + std::map net2id_local; std::map *net2id = &net2id_local; if (net2id_per_circuit) { @@ -416,12 +423,19 @@ void std_writer_impl::write (const db::Netlist *netlist, const db::LayoutT } else { if (! any) { + *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 ()) << ")"; } *mp_stream << endl; + + for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) { + *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + } + any = true; + } *mp_stream << indent << indent2; @@ -449,7 +463,15 @@ void std_writer_impl::write (const db::Netlist *netlist, const db::LayoutT if (! net.name ().empty ()) { *mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")"; } - *mp_stream << ")" << endl; + if (net.begin_properties () != net.end_properties ()) { + *mp_stream << endl; + for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) { + *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + } + *mp_stream << indent << ")" << endl; + } else { + *mp_stream << ")" << endl; + } } } @@ -470,12 +492,16 @@ void std_writer_impl::write (const db::LayoutToNetlist *l2n, const db::Sub } // each pin in one line for more than a few pins - bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1); + bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1) || subcircuit.begin_properties () != subcircuit.end_properties (); if (separate_lines) { *mp_stream << endl; } + for (db::NetlistObject::property_iterator p = subcircuit.begin_properties (); p != subcircuit.end_properties (); ++p) { + *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl; + } + for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) { const db::Net *net = subcircuit.net_for_pin (p->id ()); if (net) { @@ -614,6 +640,10 @@ void std_writer_impl::write (const db::LayoutToNetlist * /*l2n*/, const db *mp_stream << indent << indent2 << Keys::name_key << "(" << tl::to_word_or_quoted_string (device.name ()) << ")" << endl; } + for (db::NetlistObject::property_iterator p = device.begin_properties (); p != device.end_properties (); ++p) { + *mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << 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 ()) << " " << tl::sprintf ("%.12g", device.parameter_value (i->id ())) << ")" << endl; } diff --git a/src/db/db/dbPin.h b/src/db/db/dbPin.h index 5f47ce0f1..ed77d701f 100644 --- a/src/db/db/dbPin.h +++ b/src/db/db/dbPin.h @@ -72,6 +72,15 @@ public: return m_id; } + /** + * @brief Sets the name of the pin + * CAUTION: don't use this method on pins stored inside a netlist. + */ + void set_name (const std::string &name) + { + m_name = name; + } + private: friend class Circuit; @@ -82,11 +91,6 @@ private: { m_id = id; } - - void set_name (const std::string &name) - { - m_name = name; - } }; } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 4b96f916c..6d2ca28cd 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1125,7 +1125,7 @@ static void circuit_disconnect_pin1 (db::Circuit *c, const db::Pin *pin) } Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", - gsi::method ("create_pin", &db::Circuit::add_pin, gsi::arg ("name"), + gsi::method ("create_pin", (const db::Pin &(db::Circuit::*) (const std::string &)) &db::Circuit::add_pin, gsi::arg ("name"), "@brief Creates a new \\Pin object inside the circuit\n" "This object will describe a pin of the circuit. A circuit connects " "to the outside through such a pin. The pin is added after all existing " diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 5f637911d..4c7002443 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -254,7 +254,7 @@ TEST(1b_ReaderBasicShort) // verify against the input - std::string path = tmp_file ("tmp_l2nreader_2.txt"); + std::string path = tmp_file ("tmp.txt"); { tl::OutputStream stream (path); db::LayoutToNetlistStandardWriter writer (stream, true); @@ -266,6 +266,30 @@ TEST(1b_ReaderBasicShort) compare_text_files (path, au_path); } +TEST(1c_ReaderBasicShortWithProps) +{ + db::LayoutToNetlist l2n; + + std::string in_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_p.txt"); + 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, true); + writer.write (&l2n); + } + + std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_p.txt"); + + compare_text_files (path, au_path); +} + TEST(2_ReaderWithGlobalNets) { db::LayoutToNetlist l2n; @@ -278,7 +302,7 @@ TEST(2_ReaderWithGlobalNets) // verify against the input - std::string path = tmp_file ("tmp_l2nreader_2.txt"); + std::string path = tmp_file ("tmp.txt"); { tl::OutputStream stream (path); db::LayoutToNetlistStandardWriter writer (stream, false); @@ -335,7 +359,7 @@ TEST(3_ReaderAbsoluteCoordinates) // verify against the input - std::string path = tmp_file ("tmp_l2nreader_2.txt"); + std::string path = tmp_file ("tmp.txt"); { tl::OutputStream stream (path); db::LayoutToNetlistStandardWriter writer (stream, false); @@ -394,7 +418,7 @@ TEST(4_ReaderCombinedDevices) // verify against the input - std::string path = tmp_file ("tmp_l2nreader_4.txt"); + std::string path = tmp_file ("tmp.txt"); { tl::OutputStream stream (path); db::LayoutToNetlistStandardWriter writer (stream, false); diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 2db34248d..9fca0b5fd 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -217,6 +217,33 @@ TEST(1_WriterBasic) db::compare_layouts (_this, ly2, au); } + + l2n.netlist ()->begin_circuits ()->set_property (17, 42); + l2n.netlist ()->begin_circuits ()->set_property ("a_float", 0.5); + l2n.netlist ()->begin_circuits ()->set_property ("a_\"non_quoted\"_string", "s"); + + l2n.netlist ()->begin_circuits ()->begin_nets ()->set_property (17, 142); + l2n.netlist ()->begin_circuits ()->begin_nets ()->set_property ("a_float", 10.5); + l2n.netlist ()->begin_circuits ()->begin_nets ()->set_property ("a_\"non_quoted\"_string", "1s"); + + l2n.netlist ()->circuit_by_name ("INV2")->begin_devices ()->set_property (17, 242); + l2n.netlist ()->circuit_by_name ("INV2")->begin_devices ()->set_property ("a_float", 20.5); + l2n.netlist ()->circuit_by_name ("INV2")->begin_devices ()->set_property ("a_\"non_quoted\"_string", "2s"); + + l2n.netlist ()->circuit_by_name ("RINGO")->begin_subcircuits ()->set_property (17, 342); + l2n.netlist ()->circuit_by_name ("RINGO")->begin_subcircuits ()->set_property ("a_float", 30.5); + l2n.netlist ()->circuit_by_name ("RINGO")->begin_subcircuits ()->set_property ("a_\"non_quoted\"_string", "3s"); + + path = tmp_file ("tmp_l2nwriter_1p.txt"); + { + tl::OutputStream stream (path); + db::LayoutToNetlistStandardWriter writer (stream, true); + writer.write (&l2n); + } + + au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "l2n_writer_au_p.txt"); + + compare_text_files (path, au_path); } TEST(2_WriterWithGlobalNets)