Merge pull request #409 from KLayout/netlist_properties

Netlist properties
This commit is contained in:
Matthias Köfferlein 2019-11-22 23:10:39 +01:00 committed by GitHub
commit b6869e4deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 655 additions and 65 deletions

View File

@ -183,7 +183,8 @@ SOURCES = \
dbLayoutVsSchematicFormatDefs.cc \
dbLayoutVsSchematic.cc \
gsiDeclDbNetlistCrossReference.cc \
gsiDeclDbLayoutVsSchematic.cc
gsiDeclDbLayoutVsSchematic.cc \
dbNetlistObject.cc
HEADERS = \
dbArray.h \
@ -329,7 +330,8 @@ HEADERS = \
dbLayoutVsSchematicWriter.h \
dbLayoutVsSchematicReader.h \
dbLayoutVsSchematicFormatDefs.h \
dbLayoutVsSchematic.h
dbLayoutVsSchematic.h \
dbNetlistObject.h
!equals(HAVE_QT, "0") {

View File

@ -33,7 +33,7 @@ namespace db
// Circuit class implementation
Circuit::Circuit ()
: m_dont_purge (false), m_cell_index (0), mp_netlist (0),
: db::NetlistObject (), gsi::ObjectBase (), m_dont_purge (false), m_cell_index (0), mp_netlist (0),
m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices),
m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets),
@ -48,7 +48,7 @@ Circuit::Circuit ()
}
Circuit::Circuit (const db::Layout &layout, db::cell_index_type ci)
: m_name (layout.cell_name (ci)), m_dont_purge (false), m_cell_index (ci), mp_netlist (0),
: db::NetlistObject (), gsi::ObjectBase (), m_name (layout.cell_name (ci)), m_dont_purge (false), m_cell_index (ci), mp_netlist (0),
m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices),
m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets),
@ -65,7 +65,7 @@ Circuit::Circuit (const db::Layout &layout, db::cell_index_type ci)
}
Circuit::Circuit (const Circuit &other)
: gsi::ObjectBase (other), tl::Object (other), m_dont_purge (false), m_cell_index (0), mp_netlist (0),
: db::NetlistObject (other), gsi::ObjectBase (other), m_dont_purge (false), m_cell_index (0), mp_netlist (0),
m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices),
m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits),
m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets),
@ -95,6 +95,8 @@ Circuit &Circuit::operator= (const Circuit &other)
{
if (this != &other) {
db::NetlistObject::operator= (other);
clear ();
m_name = other.m_name;
@ -290,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));

View File

@ -86,7 +86,7 @@ public:
* devices.
*/
class DB_PUBLIC Circuit
: public gsi::ObjectBase, public tl::Object
: public db::NetlistObject, public gsi::ObjectBase
{
public:
typedef tl::vector<Pin> pin_list;
@ -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)
*/
@ -353,6 +359,14 @@ public:
*/
const Pin *pin_by_id (size_t id) const;
/**
* @brief Gets the pin by ID (the ID is basically the index) - non-const version
*/
Pin *pin_by_id (size_t id)
{
return const_cast<Pin *> (((const db::Circuit *) this)->pin_by_id (id));
}
/**
* @brief Gets the pin by name
*
@ -361,6 +375,14 @@ public:
*/
const Pin *pin_by_name (const std::string &name) const;
/**
* @brief Gets the pin by name - non-const version
*/
Pin *pin_by_name (const std::string &name)
{
return const_cast<Pin *> (((const db::Circuit *) this)->pin_by_name (name));
}
/**
* @brief Begin iterator for the pins of the circuit (const version)
*/

View File

@ -31,7 +31,7 @@ namespace db
// Device class implementation
Device::Device ()
: mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0)
: db::NetlistObject (), mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
@ -46,19 +46,19 @@ Device::~Device ()
}
Device::Device (DeviceClass *device_class, const std::string &name)
: mp_device_class (device_class), mp_device_abstract (0), m_name (name), m_id (0), mp_circuit (0)
: db::NetlistObject (), mp_device_class (device_class), mp_device_abstract (0), m_name (name), m_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
Device::Device (DeviceClass *device_class, DeviceAbstract *device_abstract, const std::string &name)
: mp_device_class (device_class), mp_device_abstract (device_abstract), m_name (name), m_id (0), mp_circuit (0)
: db::NetlistObject (), mp_device_class (device_class), mp_device_abstract (device_abstract), m_name (name), m_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
Device::Device (const Device &other)
: tl::Object (other), mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0)
: db::NetlistObject (other), mp_device_class (0), mp_device_abstract (0), m_id (0), mp_circuit (0)
{
operator= (other);
}
@ -66,6 +66,7 @@ Device::Device (const Device &other)
Device &Device::operator= (const Device &other)
{
if (this != &other) {
db::NetlistObject::operator= (other);
m_name = other.m_name;
m_trans = other.m_trans;
m_parameters = other.m_parameters;

View File

@ -95,7 +95,7 @@ struct DeviceAbstractRef
* a specific device class.
*/
class DB_PUBLIC Device
: public tl::Object
: public db::NetlistObject
{
public:
typedef std::vector<std::pair<size_t, size_t> > global_connections;

View File

@ -814,11 +814,20 @@ LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &
db::properties_id_type
LayoutToNetlist::make_netname_propid (db::Layout &ly, const tl::Variant &netname_prop, const db::Net &net) const
{
if (! netname_prop.is_nil ()) {
if (! netname_prop.is_nil () || net.begin_properties () != net.end_properties ()) {
db::property_names_id_type name_propnameid = ly.properties_repository ().prop_name_id (netname_prop);
db::PropertiesRepository::properties_set propset;
propset.insert (std::make_pair (name_propnameid, tl::Variant (net.expanded_name ())));
// add the user properties too (TODO: make this configurable?)
for (db::Net::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) {
db::property_names_id_type key_propnameid = ly.properties_repository ().prop_name_id (p->first);
propset.insert (std::make_pair (key_propnameid, p->second));
}
if (! netname_prop.is_nil ()) {
db::property_names_id_type name_propnameid = ly.properties_repository ().prop_name_id (netname_prop);
propset.insert (std::make_pair (name_propnameid, tl::Variant (net.expanded_name ())));
}
return ly.properties_repository ().properties_id (propset);

View File

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

View File

@ -66,7 +66,9 @@ namespace db
*
* [boundary-def]
*
* net(<id> [name]? [geometry-def]*)
* [property-def]*
*
* net(<id> [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(<name>) - specify net name [short key: I]
*
* [property-def]:
*
* property(<prop-name> <prop-value>)
* - 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(<layer> [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(<name> <value>) - defines a parameter [short key E]
* param(<name> <value>) - defines a parameter [short key: E]
* terminal(<terminal-name> <net-id>)
* - 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(<pin-id> <net-id>) - specifies connection of the pin with a net [short key: P]
*
* [trans-def]:
*
* location(<x> <y>) - location of the instance [short key Y]
* rotation(<angle>) - rotation angle (in degree, default is 0) [short key O]
* mirror - if specified, the instance is mirrored before rotation [short key M]
* scale(<mag>) - magnification (default is 1) [short key S]
* location(<x> <y>) - location of the instance [short key: Y]
* rotation(<angle>) - rotation angle (in degree, default is 0) [short key: O]
* mirror - if specified, the instance is mirrored before rotation [short key: M]
* scale(<mag>) - 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;

View File

@ -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<unsigned int, db::PolygonRef>
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<db::PolygonRef> &lc, db::Cell &cell)
LayoutToNetlistStandardReader::read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster<db::PolygonRef> &lc, db::Cell &cell)
{
m_ref = db::Point ();
while (br) {
std::pair<unsigned int, db::PolygonRef> 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<unsigned int, db::PolygonRef> 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);
}

View File

@ -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<unsigned int, db::PolygonRef> 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<db::PolygonRef> &lc, db::Cell &cell);
void read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster<db::PolygonRef> &lc, db::Cell &cell);
db::Point read_point ();
private:

View File

@ -278,6 +278,13 @@ void std_writer_impl<Keys>::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<const db::Net *, unsigned int> net2id_local;
std::map<const db::Net *, unsigned int> *net2id = &net2id_local;
if (net2id_per_circuit) {
@ -416,12 +423,19 @@ void std_writer_impl<Keys>::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<Keys>::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<Keys>::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<Keys>::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<DeviceParameterDefinition>::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;
}

View File

@ -155,19 +155,19 @@ const Pin *NetSubcircuitPinRef::pin () const
// Net class implementation
Net::Net ()
: m_cluster_id (0), mp_circuit (0)
: NetlistObject (), m_cluster_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
Net::Net (const std::string &name)
: m_cluster_id (0), mp_circuit (0)
: NetlistObject (), m_cluster_id (0), mp_circuit (0)
{
m_name = name;
}
Net::Net (const Net &other)
: tl::Object (other), m_cluster_id (0), mp_circuit (0)
: NetlistObject (other), m_cluster_id (0), mp_circuit (0)
{
operator= (other);
}
@ -176,6 +176,8 @@ Net &Net::operator= (const Net &other)
{
if (this != &other) {
db::NetlistObject::operator= (other);
clear ();
m_name = other.m_name;

View File

@ -24,6 +24,7 @@
#define _HDR_dbNet
#include "dbCommon.h"
#include "dbNetlistObject.h"
#include "tlObject.h"
@ -360,7 +361,7 @@ private:
* A net connects terminals of devices and pins or circuits
*/
class DB_PUBLIC Net
: public tl::Object
: public db::NetlistObject
{
public:
typedef std::list<NetTerminalRef> terminal_list;

View File

@ -0,0 +1,114 @@
/*
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 "dbNetlistObject.h"
namespace db
{
NetlistObject::NetlistObject ()
: tl::Object (), mp_properties (0)
{
// .. nothing yet ..
}
NetlistObject::NetlistObject (const db::NetlistObject &other)
: tl::Object (other), mp_properties (0)
{
if (other.mp_properties) {
mp_properties = new std::map<tl::Variant, tl::Variant> (*other.mp_properties);
}
}
NetlistObject::~NetlistObject ()
{
delete mp_properties;
mp_properties = 0;
}
NetlistObject &NetlistObject::operator= (const NetlistObject &other)
{
if (this != &other) {
tl::Object::operator= (other);
delete mp_properties;
mp_properties = 0;
if (other.mp_properties) {
mp_properties = new std::map<tl::Variant, tl::Variant> (*other.mp_properties);
}
}
return *this;
}
tl::Variant NetlistObject::property (const tl::Variant &key) const
{
if (! mp_properties) {
return tl::Variant ();
}
std::map<tl::Variant, tl::Variant>::const_iterator i = mp_properties->find (key);
if (i == mp_properties->end ()) {
return tl::Variant ();
} else {
return i->second;
}
}
void
NetlistObject::set_property (const tl::Variant &key, const tl::Variant &value)
{
if (value.is_nil ()) {
if (mp_properties) {
mp_properties->erase (key);
if (mp_properties->empty ()) {
delete mp_properties;
mp_properties = 0;
}
}
} else {
if (! mp_properties) {
mp_properties = new std::map<tl::Variant, tl::Variant> ();
}
(*mp_properties) [key] = value;
}
}
static NetlistObject::property_table empty_properties;
NetlistObject::property_iterator
NetlistObject::begin_properties () const
{
return mp_properties ? mp_properties->begin () : empty_properties.begin ();
}
NetlistObject::property_iterator
NetlistObject::end_properties () const
{
return mp_properties ? mp_properties->end () : empty_properties.end ();
}
}

View File

@ -0,0 +1,94 @@
/*
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
*/
#ifndef _HDR_dbNetlistObject
#define _HDR_dbNetlistObject
#include "dbCommon.h"
#include "tlObject.h"
#include "tlVariant.h"
#include <list>
#include <string>
namespace db
{
/**
* @brief A base class for a objects in the netlist
*/
class DB_PUBLIC NetlistObject
: public tl::Object
{
public:
typedef std::map<tl::Variant, tl::Variant> property_table;
typedef property_table::const_iterator property_iterator;
/**
* @brief Default constructor
*/
NetlistObject ();
/**
* @brief Copy constructor
*/
NetlistObject (const db::NetlistObject &object);
/**
* @brief Destructor
*/
~NetlistObject ();
/**
* @brief Assignment
*/
NetlistObject &operator= (const NetlistObject &other);
/**
* @brief Gets the property value for a given key
* Returns nil if there is no property for the given key.
*/
tl::Variant property (const tl::Variant &key) const;
/**
* @brief Sets the property value for a given key
* Set the value to nil to clear a specific key
*/
void set_property (const tl::Variant &key, const tl::Variant &value);
/**
* @brief Iterator for the netlist properties (begin)
*/
property_iterator begin_properties () const;
/**
* @brief Iterator for the netlist properties (end)
*/
property_iterator end_properties () const;
private:
property_table *mp_properties;
};
}
#endif

View File

@ -30,13 +30,13 @@ namespace db
// Pin class implementation
Pin::Pin ()
: m_id (0)
: db::NetlistObject (), m_id (0)
{
// .. nothing yet ..
}
Pin::Pin (const std::string &name)
: m_name (name), m_id (0)
: db::NetlistObject (), m_name (name), m_id (0)
{
// .. nothing yet ..
}

View File

@ -24,6 +24,7 @@
#define _HDR_dbPin
#include "dbCommon.h"
#include "dbNetlistObject.h"
#include <string>
@ -36,6 +37,7 @@ namespace db
* A pin is some place other nets can connect to a circuit.
*/
class DB_PUBLIC Pin
: public db::NetlistObject
{
public:
/**
@ -70,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;
@ -80,11 +91,6 @@ private:
{
m_id = id;
}
void set_name (const std::string &name)
{
m_name = name;
}
};
}

View File

@ -30,7 +30,7 @@ namespace db
// SubCircuit class implementation
SubCircuit::SubCircuit ()
: m_id (0), mp_circuit (0)
: db::NetlistObject (), m_id (0), mp_circuit (0)
{
// .. nothing yet ..
}
@ -45,13 +45,13 @@ SubCircuit::~SubCircuit()
}
SubCircuit::SubCircuit (Circuit *circuit, const std::string &name)
: m_circuit_ref (0), m_name (name), m_id (0), mp_circuit (0)
: db::NetlistObject (), m_circuit_ref (0), m_name (name), m_id (0), mp_circuit (0)
{
set_circuit_ref (circuit);
}
SubCircuit::SubCircuit (const SubCircuit &other)
: tl::Object (other), m_id (0), mp_circuit (0)
: db::NetlistObject (other), m_id (0), mp_circuit (0)
{
operator= (other);
}
@ -59,6 +59,7 @@ SubCircuit::SubCircuit (const SubCircuit &other)
SubCircuit &SubCircuit::operator= (const SubCircuit &other)
{
if (this != &other) {
db::NetlistObject::operator= (other);
m_name = other.m_name;
m_trans = other.m_trans;
set_circuit_ref (const_cast<Circuit *> (other.circuit_ref ()));

View File

@ -43,7 +43,7 @@ class Circuit;
* This class essentially is a reference to another circuit
*/
class DB_PUBLIC SubCircuit
: public tl::Object
: public db::NetlistObject
{
public:
typedef tl::vector<const Net *> connected_net_list;

View File

@ -34,7 +34,33 @@
namespace gsi
{
Class<db::Pin> decl_dbPin ("db", "Pin",
static std::vector<tl::Variant> property_keys (const db::NetlistObject *object)
{
std::vector<tl::Variant> v;
for (db::NetlistObject::property_iterator p = object->begin_properties (); p != object->end_properties (); ++p) {
v.push_back (p->first);
}
return v;
}
Class<db::NetlistObject> decl_dbNetlistObject ("db", "NetlistObject",
gsi::method ("property", &db::NetlistObject::property, gsi::arg ("key"),
"@brief Gets the property value for the given key or nil if there is no value with this key."
) +
gsi::method ("set_property", &db::NetlistObject::set_property, gsi::arg ("key"), gsi::arg ("value"),
"@brief Sets the property value for the given key.\n"
"Use a nil value to erase the property with this key."
) +
gsi::method_ext ("property_keys", &property_keys,
"@brief Gets the keys for the properties stored in this object."
),
"@brief The base class for some netlist objects.\n"
"The main purpose of this class is to supply user properties for netlist objects.\n"
"\n"
"This class has been introduced in version 0.26.2"
);
Class<db::Pin> decl_dbPin (decl_dbNetlistObject, "db", "Pin",
gsi::method ("id", &db::Pin::id,
"@brief Gets the ID of the pin.\n"
) +
@ -225,7 +251,7 @@ static void add_other_abstracts (db::Device *device, const db::DeviceAbstractRef
device->other_abstracts ().push_back (ref);
}
Class<db::Device> decl_dbDevice ("db", "Device",
Class<db::Device> decl_dbDevice (decl_dbNetlistObject, "db", "Device",
gsi::method ("device_class", &db::Device::device_class,
"@brief Gets the device class the device belongs to.\n"
) +
@ -397,7 +423,7 @@ static void subcircuit_disconnect_pin1 (db::SubCircuit *subcircuit, const db::Pi
}
}
Class<db::SubCircuit> decl_dbSubCircuit ("db", "SubCircuit",
Class<db::SubCircuit> decl_dbSubCircuit (decl_dbNetlistObject, "db", "SubCircuit",
gsi::method ("circuit_ref", (const db::Circuit *(db::SubCircuit::*) () const) &db::SubCircuit::circuit_ref,
"@brief Gets the circuit referenced by the subcircuit.\n"
) +
@ -516,7 +542,7 @@ Class<db::NetSubcircuitPinRef> decl_dbNetSubcircuitPinRef ("db", "NetSubcircuitP
"This class has been added in version 0.26."
);
Class<db::Net> decl_dbNet ("db", "Net",
Class<db::Net> decl_dbNet (decl_dbNetlistObject, "db", "Net",
gsi::method ("circuit", (db::Circuit *(db::Net::*) ()) &db::Net::circuit,
"@brief Gets the circuit the net lives in."
) +
@ -1098,8 +1124,8 @@ static void circuit_disconnect_pin1 (db::Circuit *c, const db::Pin *pin)
}
}
Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
gsi::method ("create_pin", &db::Circuit::add_pin, gsi::arg ("name"),
Class<db::Circuit> decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit",
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 "
@ -1147,11 +1173,11 @@ Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
"@brief Gets the net object for a given name.\n"
"If the ID is not a valid net name, nil is returned."
) +
gsi::method ("pin_by_id", &db::Circuit::pin_by_id, gsi::arg ("id"),
gsi::method ("pin_by_id", (db::Pin *(db::Circuit::*) (size_t)) &db::Circuit::pin_by_id, gsi::arg ("id"),
"@brief Gets the \\Pin object corresponding to a specific ID\n"
"If the ID is not a valid pin ID, nil is returned."
) +
gsi::method ("pin_by_name", &db::Circuit::pin_by_name, gsi::arg ("name"),
gsi::method ("pin_by_name", (db::Pin *(db::Circuit::*) (const std::string &)) &db::Circuit::pin_by_name, gsi::arg ("name"),
"@brief Gets the \\Pin object corresponding to a specific name\n"
"If the ID is not a valid pin name, nil is returned."
) +

View File

@ -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,60 @@ 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);
{
db::Layout ly2;
ly2.dbu (l2n.internal_layout ()->dbu ());
db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP"));
std::map<unsigned int, const db::Region *> lmap;
lmap [ly2.insert_layer (db::LayerProperties (3, 0))] = l2n.layer_by_name ("poly");
lmap [ly2.insert_layer (db::LayerProperties (3, 1))] = l2n.layer_by_name ("poly_lbl");
lmap [ly2.insert_layer (db::LayerProperties (4, 0))] = l2n.layer_by_name ("diff_cont");
lmap [ly2.insert_layer (db::LayerProperties (5, 0))] = l2n.layer_by_name ("poly_cont");
lmap [ly2.insert_layer (db::LayerProperties (6, 0))] = l2n.layer_by_name ("metal1");
lmap [ly2.insert_layer (db::LayerProperties (6, 1))] = l2n.layer_by_name ("metal1_lbl");
lmap [ly2.insert_layer (db::LayerProperties (7, 0))] = l2n.layer_by_name ("via1");
lmap [ly2.insert_layer (db::LayerProperties (8, 0))] = l2n.layer_by_name ("metal2");
lmap [ly2.insert_layer (db::LayerProperties (8, 1))] = l2n.layer_by_name ("metal2_lbl");
lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_by_name ("psd");
lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_by_name ("nsd");
db::CellMapping cm = l2n.cell_mapping_into (ly2, top2);
l2n.build_all_nets (cm, ly2, lmap, "NET_", tl::Variant (), db::LayoutToNetlist::BNH_Disconnected, 0, "DEVICE_");
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "l2n_writer_au_p.oas");
db::compare_layouts (_this, ly2, au, db::WriteOAS);
}
}
TEST(2_ReaderWithGlobalNets)
{
db::LayoutToNetlist l2n;
@ -278,7 +332,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 +389,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 +448,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);

View File

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

View File

@ -344,8 +344,16 @@ TEST(3_CircuitBasic)
EXPECT_EQ (c.pin_by_name ("p1")->name (), "p1");
EXPECT_EQ (c.pin_by_name ("doesnt_exist") == 0, true);
EXPECT_EQ (c.pin_by_name ("p2")->name (), "p2");
EXPECT_EQ (c.pin_by_id (0)->begin_properties () == c.pin_by_id (0)->end_properties (), true);
EXPECT_EQ (c.pin_by_id (0)->property (17).to_string (), "nil");
c.pin_by_id (0)->set_property (17, 42);
EXPECT_EQ (c.pin_by_id (0)->begin_properties () == c.pin_by_id (0)->end_properties (), false);
EXPECT_EQ (c.pin_by_id (0)->begin_properties ()->second.to_string (), "42");
EXPECT_EQ (c.pin_by_id (0)->property (17).to_string (), "42");
db::Circuit c2 = c;
EXPECT_EQ (c2.pin_by_id (0)->property (17).to_string (), "42");
EXPECT_EQ (c2.name (), "name");
EXPECT_EQ (pins2string (c), "p1#0,p2#1");
@ -1419,3 +1427,90 @@ TEST(22_BlankCircuit)
"end;\n"
);
}
TEST(23_NetlistObject)
{
db::NetlistObject nlo;
nlo.set_property (1, "hello");
EXPECT_EQ (nlo.property ("key").to_string (), "nil");
EXPECT_EQ (nlo.property (1).to_string (), "hello");
nlo.set_property ("key", 42);
EXPECT_EQ (nlo.property ("key").to_string (), "42");
nlo.set_property ("key", tl::Variant ());
EXPECT_EQ (nlo.property ("key").to_string (), "nil");
db::Net net ("net_name");
net.set_property (1, "hello");
EXPECT_EQ (net.property ("key").to_string (), "nil");
EXPECT_EQ (net.property (1).to_string (), "hello");
db::Net net2 (net);
EXPECT_EQ (net.property ("key").to_string (), "nil");
EXPECT_EQ (net.property (1).to_string (), "hello");
db::Net net3;
EXPECT_EQ (net3.property ("key").to_string (), "nil");
EXPECT_EQ (net3.property (1).to_string (), "nil");
net3 = net2;
EXPECT_EQ (net3.property (1).to_string (), "hello");
db::SubCircuit sc;
sc.set_property (1, "hello");
EXPECT_EQ (sc.property ("key").to_string (), "nil");
EXPECT_EQ (sc.property (1).to_string (), "hello");
db::SubCircuit sc2 (sc);
EXPECT_EQ (sc.property ("key").to_string (), "nil");
EXPECT_EQ (sc.property (1).to_string (), "hello");
db::SubCircuit sc3;
EXPECT_EQ (sc3.property ("key").to_string (), "nil");
EXPECT_EQ (sc3.property (1).to_string (), "nil");
sc3 = sc2;
EXPECT_EQ (sc3.property (1).to_string (), "hello");
db::Device dev;
dev.set_property (1, "hello");
EXPECT_EQ (dev.property ("key").to_string (), "nil");
EXPECT_EQ (dev.property (1).to_string (), "hello");
db::Device dev2 (dev);
EXPECT_EQ (dev.property ("key").to_string (), "nil");
EXPECT_EQ (dev.property (1).to_string (), "hello");
db::Device dev3;
EXPECT_EQ (dev3.property ("key").to_string (), "nil");
EXPECT_EQ (dev3.property (1).to_string (), "nil");
dev3 = dev2;
EXPECT_EQ (dev3.property (1).to_string (), "hello");
db::Circuit circuit;
circuit.set_property (1, "hello");
EXPECT_EQ (circuit.property ("key").to_string (), "nil");
EXPECT_EQ (circuit.property (1).to_string (), "hello");
db::Circuit circuit2 (circuit);
EXPECT_EQ (circuit.property ("key").to_string (), "nil");
EXPECT_EQ (circuit.property (1).to_string (), "hello");
db::Circuit circuit3;
EXPECT_EQ (circuit3.property ("key").to_string (), "nil");
EXPECT_EQ (circuit3.property (1).to_string (), "nil");
circuit3 = circuit2;
EXPECT_EQ (circuit3.property (1).to_string (), "hello");
db::Pin pin ("pin_name");
pin.set_property (1, "hello");
EXPECT_EQ (pin.property ("key").to_string (), "nil");
EXPECT_EQ (pin.property (1).to_string (), "hello");
db::Pin pin2 (pin);
EXPECT_EQ (pin.property ("key").to_string (), "nil");
EXPECT_EQ (pin.property (1).to_string (), "hello");
db::Pin pin3;
EXPECT_EQ (pin3.property ("key").to_string (), "nil");
EXPECT_EQ (pin3.property (1).to_string (), "nil");
pin3 = pin2;
EXPECT_EQ (pin3.property (1).to_string (), "hello");
}

BIN
testdata/algo/l2n_writer_au_p.oas vendored Normal file

Binary file not shown.

View File

@ -25,6 +25,24 @@ load("test_prologue.rb")
class DBNetlist_TestClass < TestBase
def test_0_NetlistObject
nlo = RBA::NetlistObject::new
assert_equal(nlo.property(17), nil)
assert_equal(nlo.property_keys.inspect, "[]")
nlo.set_property(17, 42)
assert_equal(nlo.property_keys.inspect, "[17]")
assert_equal(nlo.property(17), 42)
nlo2 = nlo.dup
assert_equal(nlo2.property(17), 42)
nlo.set_property(17, nil)
assert_equal(nlo.property_keys.inspect, "[]")
assert_equal(nlo.property(17), nil)
assert_equal(nlo2.property(17), 42)
end
def test_1_NetlistBasicCircuit
nl = RBA::Netlist::new
@ -141,6 +159,10 @@ class DBNetlist_TestClass < TestBase
p1 = c.create_pin("A")
p2 = c.create_pin("B")
assert_equal(p1.property(17), nil)
p1.set_property(17, 42)
assert_equal(p1.property(17), 42)
assert_equal(p1.id, 0)
assert_equal(p2.id, 1)
@ -188,6 +210,10 @@ class DBNetlist_TestClass < TestBase
assert_equal(c.device_by_id(2).inspect, "nil")
assert_equal(c.device_by_name("doesnt_exist").inspect, "nil")
assert_equal(d1.property(17), nil)
d1.set_property(17, 42)
assert_equal(d1.property(17), 42)
d2 = c.create_device(dc)
assert_equal(d2.device_class.id, dc.id)
assert_equal(d2.device_class.object_id, dc.object_id) # by virtue of Ruby-to-C++ object mapping
@ -215,6 +241,10 @@ class DBNetlist_TestClass < TestBase
net = c.create_net("NET")
assert_equal(net.property(17), nil)
net.set_property(17, 42)
assert_equal(net.property(17), 42)
assert_equal(net.is_floating?, true)
assert_equal(net.is_internal?, false)
assert_equal(net.terminal_count, 0)
@ -370,6 +400,10 @@ class DBNetlist_TestClass < TestBase
assert_equal(c.subcircuit_by_id(2).inspect, "nil")
assert_equal(c.subcircuit_by_name("doesnt_exist").inspect, "nil")
assert_equal(sc1.property(17), nil)
sc1.set_property(17, 42)
assert_equal(sc1.property(17), 42)
refs = []
cc.each_ref { |r| refs << r.name }
assert_equal(refs.join(","), "SC1")
@ -564,6 +598,10 @@ class DBNetlist_TestClass < TestBase
c.cell_index = 42
assert_equal(c.cell_index, 42)
assert_equal(c.property(17), nil)
c.set_property(17, 42)
assert_equal(c.property(17), 42)
pina1 = c.create_pin("A1")
pina2 = c.create_pin("A2")
pinb1 = c.create_pin("B1")
@ -581,6 +619,11 @@ class DBNetlist_TestClass < TestBase
assert_equal(c.pin_by_id(17).inspect, "nil")
assert_equal(c.pin_by_name("DOESNOTEXIST").inspect, "nil")
assert_equal(c.pin_by_id(0).property(17), nil)
c.pin_by_id(0).set_property(17, 42)
assert_equal(c.pin_by_id(0).property(17), 42)
assert_equal(c.pin_by_name("A1").property(17), 42)
names = []
c.each_pin { |p| names << p.name }
assert_equal(names, [ "A1", "A2", "B1", "B2" ])