mirror of https://github.com/KLayout/klayout.git
WIP: Refined output format for l2n
This commit is contained in:
parent
4cb8982ca2
commit
438f50091f
|
|
@ -75,6 +75,15 @@ Device &Device::operator= (const Device &other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
std::string Device::expanded_name () const
|
||||
{
|
||||
if (name ().empty ()) {
|
||||
return "$" + tl::to_string (id ());
|
||||
} else {
|
||||
return name ();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::set_circuit (Circuit *circuit)
|
||||
{
|
||||
mp_circuit = circuit;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ public:
|
|||
{
|
||||
mp_device_class = dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device model
|
||||
*/
|
||||
|
|
@ -157,6 +158,12 @@ public:
|
|||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a name which always non-empty
|
||||
* This method will pick a name like "$<id>" if the explicit name is empty.
|
||||
*/
|
||||
std::string expanded_name () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the device position
|
||||
* The device position should be the center of the recognition shape or something similar.
|
||||
|
|
|
|||
|
|
@ -29,19 +29,25 @@ namespace db
|
|||
// --------------------------------------------------------------------------------
|
||||
// DeviceModel class implementation
|
||||
|
||||
DeviceModel::DeviceModel ()
|
||||
: m_name (), mp_device_class (0), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeviceModel::~DeviceModel ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeviceModel::DeviceModel (const std::string &name)
|
||||
: m_name (name), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
DeviceModel::DeviceModel (db::DeviceClass *device_class, const std::string &name)
|
||||
: m_name (name), mp_device_class (device_class), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
DeviceModel::DeviceModel (const DeviceModel &other)
|
||||
: tl::Object (other), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
: tl::Object (other), mp_device_class (0), m_cell_index (std::numeric_limits<db::cell_index_type>::max ()), mp_netlist (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
|
@ -50,6 +56,7 @@ DeviceModel &DeviceModel::operator= (const DeviceModel &other)
|
|||
{
|
||||
if (this != &other) {
|
||||
m_name = other.m_name;
|
||||
mp_device_class = other.mp_device_class;
|
||||
m_cell_index = other.m_cell_index;
|
||||
m_terminal_cluster_ids = other.m_terminal_cluster_ids;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,10 +46,15 @@ class DB_PUBLIC DeviceModel
|
|||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
DeviceModel ();
|
||||
|
||||
/**
|
||||
* @brief The constructor
|
||||
*/
|
||||
DeviceModel (const std::string &name = std::string ());
|
||||
DeviceModel (db::DeviceClass *device_class, const std::string &name = std::string ());
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
|
|
@ -66,6 +71,22 @@ public:
|
|||
*/
|
||||
~DeviceModel ();
|
||||
|
||||
/**
|
||||
* @brief Gets the device class
|
||||
*/
|
||||
const DeviceClass *device_class () const
|
||||
{
|
||||
return mp_device_class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the device class
|
||||
*/
|
||||
void set_device_class (DeviceClass *dc)
|
||||
{
|
||||
mp_device_class = dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the netlist the device lives in (const version)
|
||||
* This pointer is 0 if the device model isn't added to a netlist
|
||||
|
|
@ -127,6 +148,7 @@ private:
|
|||
friend class Netlist;
|
||||
|
||||
std::string m_name;
|
||||
db::DeviceClass *mp_device_class;
|
||||
db::cell_index_type m_cell_index;
|
||||
std::vector<size_t> m_terminal_cluster_ids;
|
||||
Netlist *mp_netlist;
|
||||
|
|
|
|||
|
|
@ -39,49 +39,66 @@ namespace db
|
|||
* non-alpha (e.g. "*") or empty.
|
||||
* Single-valued attributes can be given without brackets.
|
||||
* All dimensions are in units of database unit.
|
||||
* The file follows the declaration-before-use principle
|
||||
* (circuits before subcircuits, nets before use ...)
|
||||
*
|
||||
* Global statements:
|
||||
*
|
||||
* version(<number>) - file format version
|
||||
* description(<text>) - an arbitrary description text
|
||||
* dbu(<dbu>) - specifies the database unit [short key: D]
|
||||
* unit(<unit>) - specifies the database unit [short key: U]
|
||||
* top(<circuit>) - specifies the name of the top circuit [short key: T]
|
||||
* layer(<name>) - define a layer [short key: L]
|
||||
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
|
||||
* global(<layer> <net> ...) - connects a layer with the given global nets [short key: G]
|
||||
* circuit(<name> ...) - defines a circuit (cell) [short key: X]
|
||||
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
|
||||
* device(<name> <class> [device-footprint-def])
|
||||
* - device footprint [short key: D]
|
||||
*
|
||||
* Inside the circuit:
|
||||
* [circuit-def]:
|
||||
*
|
||||
* net(<name> ...) - specifies net geometry [short key: N]
|
||||
* device(<name> <class> ...) - defines a device [short key: D]
|
||||
* subcircuit(<name> ...) - defines a subcircuit [short key: X]
|
||||
* net(<name> [geometry-def]) - net geometry [short key: N]
|
||||
* A net declaration shall be there also if no geometry
|
||||
* is present
|
||||
* pin(<name> <net-name>) - outgoing pin connection [short key: P]
|
||||
* device(<name> <class> [device-def])
|
||||
* - device with connections [short key: D]
|
||||
* subcircuit(<name> [subcircuit-def])
|
||||
* - subcircuit with connections [short key: X]
|
||||
*
|
||||
* Inside a net:
|
||||
* [geometry-def]:
|
||||
*
|
||||
* polygon(<layer> <x> <y> ...) - defines a polygon [short key: P]
|
||||
* rect(<layer> <left> <bottom> <right> <top>)
|
||||
* - defines a rectangle [short key: R]
|
||||
*
|
||||
* Inside a device:
|
||||
* [device-footprint-def]:
|
||||
*
|
||||
* terminal(<terminal-name> [geometry-def])
|
||||
* - specifies the terminal geometry [short key: empty]
|
||||
*
|
||||
* [device-def]:
|
||||
*
|
||||
* param(<name> <value>) - defines a parameter [short key P]
|
||||
* footprint(<name>) - links to a geometrical device footprint on top level [short key F]
|
||||
* terminal(<terminal-name> <net-name>)
|
||||
* - specifies connection of the terminal with
|
||||
* a net (short key: empty)
|
||||
* location(<x> <y>) - location of the device [short key L]
|
||||
*
|
||||
* Inside a subcircuit:
|
||||
* [subcircuit-def]:
|
||||
*
|
||||
* location(<x> <y>) - location of the subcircuit [short key L]
|
||||
* rotation(<angle>) - rotation angle [short key O]
|
||||
* mirror - if specified, the instance is mirrored before rotation [short key M]
|
||||
* scale(<mag>) - magnification [short key *]
|
||||
* pin(<pin-name> <net-name>) - specifies connection of the pin with a net
|
||||
* pin(<pin-name> <net-name>) - specifies connection of the pin with a net [short key: P]
|
||||
*/
|
||||
|
||||
static std::string version_key ("version");
|
||||
static std::string description_key ("description");
|
||||
static std::string top_key ("top");
|
||||
static std::string dbu_key ("dbu");
|
||||
static std::string unit_key ("unit");
|
||||
static std::string layer_key ("layer");
|
||||
static std::string text_key ("text");
|
||||
static std::string connect_key ("connect");
|
||||
|
|
@ -93,6 +110,7 @@ static std::string subcircuit_key ("subcircuit");
|
|||
static std::string polygon_key ("polygon");
|
||||
static std::string rect_key ("rect");
|
||||
static std::string terminal_key ("terminal");
|
||||
static std::string footprint_key ("footprint");
|
||||
static std::string label_key ("label");
|
||||
static std::string param_key ("param");
|
||||
static std::string location_key ("location");
|
||||
|
|
@ -122,12 +140,17 @@ static std::string name_for_layer (const db::Layout *layout, unsigned int l)
|
|||
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
|
||||
{
|
||||
const int version = 1;
|
||||
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
const db::Netlist *nl = l2n->netlist ();
|
||||
|
||||
*mp_stream << "# General" << endl;
|
||||
*mp_stream << version_key << "(" << version << ")" << endl;
|
||||
*mp_stream << top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (l2n->internal_top_cell ()->cell_index ())) << ")" << endl;
|
||||
*mp_stream << dbu_key << "(" << ly->dbu () << ")" << endl;
|
||||
*mp_stream << unit_key << "(" << ly->dbu () << ")" << endl;
|
||||
|
||||
*mp_stream << endl << "# Layers" << endl;
|
||||
for (db::Connectivity::layer_iterator l = l2n->connectivity ().begin_layers (); l != l2n->connectivity ().end_layers (); ++l) {
|
||||
|
||||
*mp_stream << layer_key << "(" << name_for_layer (ly, *l) << ")" << endl;
|
||||
|
|
@ -137,7 +160,7 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
|
|||
if (cb != ce) {
|
||||
*mp_stream << connect_key << "(" << name_for_layer (ly, *l);
|
||||
for (db::Connectivity::layer_iterator c = l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
|
||||
*mp_stream << " " << name_for_layer (ly, *l);
|
||||
*mp_stream << " " << name_for_layer (ly, *c);
|
||||
}
|
||||
*mp_stream << ")" << endl;
|
||||
}
|
||||
|
|
@ -154,7 +177,18 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n)
|
|||
|
||||
}
|
||||
|
||||
for (db::Netlist::const_circuit_iterator x = nl->begin_circuits (); x != nl->end_circuits (); ++x) {
|
||||
*mp_stream << endl << "# Device footprints" << endl;
|
||||
for (db::Netlist::const_device_model_iterator m = nl->begin_device_models (); m != nl->end_device_models (); ++m) {
|
||||
if (m->device_class ()) {
|
||||
*mp_stream << device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
|
||||
write (l2n, *m);
|
||||
*mp_stream << ")" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Netlist::const_top_down_circuit_iterator i = nl->begin_top_down (); i != nl->end_top_down (); ++i) {
|
||||
const db::Circuit *x = *i;
|
||||
*mp_stream << endl << "# Circuit " << x->name () << endl;
|
||||
*mp_stream << circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
|
||||
write (l2n, *x);
|
||||
*mp_stream << ")" << endl;
|
||||
|
|
@ -167,6 +201,13 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
|
|||
write (l2n, *n);
|
||||
}
|
||||
|
||||
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
|
||||
const db::Net *net = circuit.net_for_pin (p->id ());
|
||||
if (net) {
|
||||
*mp_stream << indent1 << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) {
|
||||
write (l2n, *d);
|
||||
}
|
||||
|
|
@ -185,6 +226,31 @@ void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr)
|
|||
}
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardWriter::write (const db::PolygonRef *s, const std::string &lname)
|
||||
{
|
||||
const db::Polygon &poly = s->obj ();
|
||||
if (poly.is_box ()) {
|
||||
|
||||
db::Box box = s->trans () * poly.box ();
|
||||
*mp_stream << rect_key << "(" << lname;
|
||||
*mp_stream << " " << box.left () << " " << box.bottom ();
|
||||
*mp_stream << " " << box.right () << " " << box.top ();
|
||||
*mp_stream << ")";
|
||||
|
||||
} else {
|
||||
|
||||
*mp_stream << polygon_key << "(" << lname;
|
||||
if (poly.holes () > 0) {
|
||||
db::SimplePolygon sp (poly);
|
||||
write_points (*mp_stream, sp, s->trans ());
|
||||
} else {
|
||||
write_points (*mp_stream, poly, s->trans ());
|
||||
}
|
||||
*mp_stream << ")";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Net &net)
|
||||
{
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
|
|
@ -192,47 +258,39 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const
|
|||
const db::Circuit *circuit = net.circuit ();
|
||||
const db::Connectivity &conn = l2n->connectivity ();
|
||||
|
||||
*mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl;
|
||||
bool any = false;
|
||||
|
||||
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
|
||||
|
||||
const db::local_cluster<db::PolygonRef> &lc = clusters.clusters_per_cell (circuit->cell_index ()).cluster_by_id (net.cluster_id ());
|
||||
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) {
|
||||
|
||||
*mp_stream << indent2;
|
||||
|
||||
const db::Polygon &poly = s->obj ();
|
||||
if (poly.is_box ()) {
|
||||
|
||||
db::Box box = s->trans () * poly.box ();
|
||||
*mp_stream << rect_key << "(" << name_for_layer (ly, *l);
|
||||
*mp_stream << " " << box.left () << " " << box.bottom ();
|
||||
*mp_stream << " " << box.right () << " " << box.top ();
|
||||
*mp_stream << ")";
|
||||
|
||||
} else {
|
||||
|
||||
*mp_stream << polygon_key << "(" << name_for_layer (ly, *l);
|
||||
if (poly.holes () > 0) {
|
||||
db::SimplePolygon sp (poly);
|
||||
write_points (*mp_stream, sp, s->trans ());
|
||||
} else {
|
||||
write_points (*mp_stream, poly, s->trans ());
|
||||
}
|
||||
*mp_stream << ")";
|
||||
|
||||
if (! any) {
|
||||
*mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << endl;
|
||||
any = true;
|
||||
}
|
||||
|
||||
*mp_stream << indent2;
|
||||
write (s.operator-> (), name_for_layer (ly, *l));
|
||||
*mp_stream << endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*mp_stream << indent1 << ")" << endl;
|
||||
if (any) {
|
||||
*mp_stream << indent1 << ")" << endl;
|
||||
} else {
|
||||
*mp_stream << indent1 << net_key << "(" << tl::to_word_or_quoted_string (net.expanded_name ()) << ")" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::SubCircuit &subcircuit)
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit)
|
||||
{
|
||||
*mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ());
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
double dbu = ly->dbu ();
|
||||
|
||||
*mp_stream << indent1 << subcircuit_key << "(" << tl::to_word_or_quoted_string (subcircuit.expanded_name ());
|
||||
|
||||
const db::DCplxTrans &tr = subcircuit.trans ();
|
||||
if (tr.is_mag ()) {
|
||||
|
|
@ -244,24 +302,27 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/,
|
|||
if (fabs (tr.angle ()) > 1e-6) {
|
||||
*mp_stream << " " << rotation_key << "(" << tr.angle () << ")";
|
||||
}
|
||||
*mp_stream << " " << location_key << "(" << tr.disp ().x () << " " << tr.disp ().y () << ")";
|
||||
*mp_stream << " " << location_key << "(" << tr.disp ().x () / dbu << " " << tr.disp ().y () / dbu << ")";
|
||||
|
||||
// each pin in one line for more than 16 pins
|
||||
bool separate_lines = (subcircuit.circuit ()->pin_count () > 16);
|
||||
// each pin in one line for more than a few pins
|
||||
bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1);
|
||||
|
||||
if (separate_lines) {
|
||||
*mp_stream << endl;
|
||||
}
|
||||
|
||||
for (db::Circuit::const_pin_iterator p = subcircuit.circuit ()->begin_pins (); p != subcircuit.circuit ()->end_pins (); ++p) {
|
||||
if (separate_lines) {
|
||||
*mp_stream << indent2;
|
||||
} else {
|
||||
*mp_stream << " ";
|
||||
}
|
||||
*mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_word_or_quoted_string (subcircuit.net_for_pin (p->id ())->expanded_name ()) << ")";
|
||||
if (separate_lines) {
|
||||
*mp_stream << 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) {
|
||||
if (separate_lines) {
|
||||
*mp_stream << indent2;
|
||||
} else {
|
||||
*mp_stream << " ";
|
||||
}
|
||||
*mp_stream << pin_key << "(" << tl::to_word_or_quoted_string (p->expanded_name ()) << " " << tl::to_word_or_quoted_string (net->expanded_name ()) << ")";
|
||||
if (separate_lines) {
|
||||
*mp_stream << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,24 +333,57 @@ void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/,
|
|||
*mp_stream << ")" << endl;
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist * /*l2n*/, const db::Device &device)
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model)
|
||||
{
|
||||
// @@@ TODO: add location
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = device_model.device_class ()->terminal_definitions ();
|
||||
|
||||
*mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.name ());
|
||||
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ());
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
const db::hier_clusters<db::PolygonRef> &clusters = l2n->net_clusters ();
|
||||
const db::Connectivity &conn = l2n->connectivity ();
|
||||
|
||||
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
|
||||
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
|
||||
|
||||
const db::local_cluster<db::PolygonRef> &lc = clusters.clusters_per_cell (device_model.cell_index ()).cluster_by_id (device_model.cluster_id_for_terminal (t->id ()));
|
||||
for (db::local_cluster<db::PolygonRef>::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) {
|
||||
|
||||
*mp_stream << indent1;
|
||||
write (s.operator-> (), name_for_layer (ly, *l));
|
||||
*mp_stream << endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutToNetlistStandardWriter::write (const db::LayoutToNetlist *l2n, const db::Device &device)
|
||||
{
|
||||
const db::Layout *ly = l2n->internal_layout ();
|
||||
double dbu = ly->dbu ();
|
||||
|
||||
*mp_stream << indent1 << device_key << "(" << tl::to_word_or_quoted_string (device.expanded_name ());
|
||||
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl;
|
||||
|
||||
*mp_stream << indent2 << location_key << "(" << device.position ().x () / dbu << " " << device.position ().y () / dbu << ")" << endl;
|
||||
|
||||
if (device.device_model ()) {
|
||||
*mp_stream << indent2 << footprint_key << "(" << tl::to_word_or_quoted_string (device.device_model ()->name ()) << ")" << endl;
|
||||
}
|
||||
|
||||
const std::vector<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
|
||||
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
|
||||
*mp_stream << " " << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")";
|
||||
*mp_stream << indent2 << param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << device.parameter_value (i->id ()) << ")" << endl;
|
||||
}
|
||||
|
||||
const std::vector<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
|
||||
for (std::vector<DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
|
||||
*mp_stream << " " << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")";
|
||||
*mp_stream << indent2 << terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::to_word_or_quoted_string (device.net_for_terminal (i->id ())->expanded_name ()) << ")" << endl;
|
||||
}
|
||||
|
||||
*mp_stream << ")" << endl;
|
||||
*mp_stream << indent1 << ")" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#define HDR_dbLayoutToNetlistWriter
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "dbPolygon.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
namespace db
|
||||
|
|
@ -34,6 +35,7 @@ class Net;
|
|||
class Circuit;
|
||||
class SubCircuit;
|
||||
class Device;
|
||||
class DeviceModel;
|
||||
|
||||
/**
|
||||
* @brief The base class for a LayoutToNetlist writer
|
||||
|
|
@ -65,6 +67,8 @@ private:
|
|||
void write (const db::LayoutToNetlist *l2n, const db::Net &net);
|
||||
void write (const db::LayoutToNetlist *l2n, const db::SubCircuit &subcircuit);
|
||||
void write (const db::LayoutToNetlist *l2n, const db::Device &device);
|
||||
void write (const db::LayoutToNetlist *l2n, const db::DeviceModel &device_model);
|
||||
void write (const db::PolygonRef *s, const std::string &lname);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ void NetlistDeviceExtractor::push_new_devices ()
|
|||
std::string cell_name = "D$" + mp_device_class->name ();
|
||||
db::Cell &device_cell = mp_layout->cell (mp_layout->add_cell (cell_name.c_str ()));
|
||||
|
||||
db::DeviceModel *dm = new db::DeviceModel (mp_layout->cell_name (device_cell.cell_index ()));
|
||||
db::DeviceModel *dm = new db::DeviceModel (mp_device_class, mp_layout->cell_name (device_cell.cell_index ()));
|
||||
m_netlist->add_device_model (dm);
|
||||
dm->set_cell_index (device_cell.cell_index ());
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#include "dbPin.h"
|
||||
#include "tlString.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
|
@ -40,4 +41,13 @@ Pin::Pin (const std::string &name)
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
std::string Pin::expanded_name () const
|
||||
{
|
||||
if (name ().empty ()) {
|
||||
return "$" + tl::to_string (id ());
|
||||
} else {
|
||||
return name ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ public:
|
|||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a name which always non-empty
|
||||
* This method will pick a name like "$<id>" if the explicit name is empty.
|
||||
*/
|
||||
std::string expanded_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the ID of the pin (only pins inside circuits have valid ID's)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -74,6 +74,15 @@ void SubCircuit::set_name (const std::string &n)
|
|||
}
|
||||
}
|
||||
|
||||
std::string SubCircuit::expanded_name () const
|
||||
{
|
||||
if (name ().empty ()) {
|
||||
return "$" + tl::to_string (id ());
|
||||
} else {
|
||||
return name ();
|
||||
}
|
||||
}
|
||||
|
||||
void SubCircuit::set_trans (const db::DCplxTrans &t)
|
||||
{
|
||||
m_trans = t;
|
||||
|
|
|
|||
|
|
@ -134,6 +134,12 @@ public:
|
|||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a name which always non-empty
|
||||
* This method will pick a name like "$<id>" if the explicit name is empty.
|
||||
*/
|
||||
std::string expanded_name () const;
|
||||
|
||||
/**
|
||||
* @brief Sets the transformation describing the subcircuit
|
||||
*
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include "gsiDecl.h"
|
||||
#include "dbLayoutToNetlist.h"
|
||||
#include "dbLayoutToNetlistWriter.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
namespace gsi
|
||||
{
|
||||
|
|
@ -58,6 +60,13 @@ static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMappin
|
|||
l2n->build_all_nets (cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ());
|
||||
}
|
||||
|
||||
static void write_l2n (const db::LayoutToNetlist *l2n, const std::string &path)
|
||||
{
|
||||
tl::OutputStream stream (path);
|
||||
db::LayoutToNetlistStandardWriter writer (stream);
|
||||
writer.write (l2n);
|
||||
}
|
||||
|
||||
Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
||||
gsi::constructor ("new", &make_l2n, gsi::arg ("iter"),
|
||||
"@brief The constructor\n"
|
||||
|
|
@ -254,6 +263,10 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
|||
"See the description of the other \\probe_net variant.\n"
|
||||
"This variant accepts a database-unit location. The location is given in the\n"
|
||||
"coordinate space of the initial cell.\n"
|
||||
) +
|
||||
gsi::method_ext ("write", &write_l2n, gsi::arg ("path"),
|
||||
"@brief Writes the extracted netlist to a file.\n"
|
||||
"This method employs the native format of KLayout.\n"
|
||||
),
|
||||
"@brief A generic framework for extracting netlists from layouts\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
/*
|
||||
|
||||
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 "dbLayoutToNetlist.h"
|
||||
#include "dbLayoutToNetlistWriter.h"
|
||||
#include "dbStream.h"
|
||||
#include "dbCommonReader.h"
|
||||
#include "dbNetlistDeviceExtractorClasses.h"
|
||||
|
||||
#include "tlUnitTest.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlFileUtils.h"
|
||||
|
||||
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_WriterBasic)
|
||||
{
|
||||
db::Layout ly;
|
||||
db::LayerMap lmap;
|
||||
|
||||
unsigned int nwell = define_layer (ly, lmap, 1);
|
||||
unsigned int active = define_layer (ly, lmap, 2);
|
||||
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<db::CommonReaderOptions> ().layer_map = lmap;
|
||||
options.get_options<db::CommonReaderOptions> ().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, "device_extract_l1.gds");
|
||||
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (ly, options);
|
||||
}
|
||||
|
||||
db::Cell &tc = ly.cell (*ly.begin_top_down ());
|
||||
db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
|
||||
|
||||
std::auto_ptr<db::Region> rnwell (l2n.make_layer (nwell));
|
||||
std::auto_ptr<db::Region> ractive (l2n.make_layer (active));
|
||||
std::auto_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly));
|
||||
std::auto_ptr<db::Region> rpoly_lbl (l2n.make_text_layer (poly_lbl));
|
||||
std::auto_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont));
|
||||
std::auto_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont));
|
||||
std::auto_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1));
|
||||
std::auto_ptr<db::Region> rmetal1_lbl (l2n.make_text_layer (metal1_lbl));
|
||||
std::auto_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1));
|
||||
std::auto_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2));
|
||||
std::auto_ptr<db::Region> rmetal2_lbl (l2n.make_text_layer (metal2_lbl));
|
||||
|
||||
// derived regions
|
||||
|
||||
db::Region rpactive = *ractive & *rnwell;
|
||||
db::Region rpgate = rpactive & *rpoly;
|
||||
db::Region rpsd = rpactive - rpgate;
|
||||
|
||||
db::Region rnactive = *ractive - *rnwell;
|
||||
db::Region rngate = rnactive & *rpoly;
|
||||
db::Region rnsd = rnactive - rngate;
|
||||
|
||||
db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS");
|
||||
db::NetlistDeviceExtractorMOS3Transistor 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
|
||||
l2n.extract_devices (pmos_ex, dl);
|
||||
|
||||
dl["SD"] = &rnsd;
|
||||
dl["G"] = &rngate;
|
||||
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
|
||||
l2n.extract_devices (nmos_ex, dl);
|
||||
|
||||
// return the computed layers into the original layout and write it for debugging purposes
|
||||
// NOTE: this will include the device layers too
|
||||
|
||||
unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate
|
||||
unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain
|
||||
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
|
||||
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
|
||||
unsigned int lpoly = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal
|
||||
|
||||
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);
|
||||
rpoly->insert_into (&ly, tc.cell_index (), lpoly);
|
||||
|
||||
// net extraction
|
||||
|
||||
// Intra-layer
|
||||
l2n.connect (rpsd);
|
||||
l2n.connect (rnsd);
|
||||
l2n.connect (*rpoly);
|
||||
l2n.connect (*rdiff_cont);
|
||||
l2n.connect (*rpoly_cont);
|
||||
l2n.connect (*rmetal1);
|
||||
l2n.connect (*rvia1);
|
||||
l2n.connect (*rmetal2);
|
||||
// Inter-layer
|
||||
l2n.connect (rpsd, *rdiff_cont);
|
||||
l2n.connect (rnsd, *rdiff_cont);
|
||||
l2n.connect (*rpoly, *rpoly_cont);
|
||||
l2n.connect (*rpoly_cont, *rmetal1);
|
||||
l2n.connect (*rdiff_cont, *rmetal1);
|
||||
l2n.connect (*rmetal1, *rvia1);
|
||||
l2n.connect (*rvia1, *rmetal2);
|
||||
l2n.connect (*rpoly, *rpoly_lbl); // attaches labels
|
||||
l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels
|
||||
l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels
|
||||
|
||||
// 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);
|
||||
|
||||
l2n.extract_netlist ();
|
||||
|
||||
tl::OutputMemoryStream mem;
|
||||
{
|
||||
tl::OutputStream stream (mem);
|
||||
db::LayoutToNetlistStandardWriter writer (stream);
|
||||
writer.write (&l2n);
|
||||
}
|
||||
|
||||
// TODO: too big for inlined text ...
|
||||
#if 0
|
||||
EXPECT_EQ (std::string (mem.data (), mem.size ()),
|
||||
""
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
|
@ -502,6 +502,8 @@ TEST(4_NetlistSubcircuits)
|
|||
nl->add_device_class (dc);
|
||||
|
||||
db::DeviceModel *dm = new db::DeviceModel ();
|
||||
dm->set_device_class (dc);
|
||||
EXPECT_EQ (dm->device_class () == dc, true);
|
||||
dm->set_name ("dm2");
|
||||
dm->set_cell_index (42);
|
||||
dm->set_cluster_id_for_terminal (0, 17);
|
||||
|
|
@ -1044,7 +1046,8 @@ TEST(13_DeviceModel)
|
|||
{
|
||||
db::Netlist nl;
|
||||
|
||||
db::DeviceModel *dm = new db::DeviceModel ("name");
|
||||
db::DeviceModel *dm = new db::DeviceModel (0, "name");
|
||||
EXPECT_EQ (dm->device_class () == 0, true);
|
||||
EXPECT_EQ (dm->name (), "name");
|
||||
dm->set_name ("name2");
|
||||
EXPECT_EQ (dm->name (), "name2");
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ SOURCES = \
|
|||
dbNetlistExtractorTests.cc \
|
||||
dbNetlistDeviceExtractorTests.cc \
|
||||
dbNetlistDeviceClassesTests.cc \
|
||||
dbLayoutToNetlistTests.cc
|
||||
dbLayoutToNetlistTests.cc \
|
||||
dbLayoutToNetlistWriterTests.cc
|
||||
|
||||
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
|
||||
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC
|
||||
|
|
|
|||
Loading…
Reference in New Issue