2018-12-30 17:53:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
KLayout Layout Viewer
|
2019-01-08 01:09:25 +01:00
|
|
|
Copyright (C) 2006-2019 Matthias Koefferlein
|
2018-12-30 17:53:46 +01:00
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "dbCommon.h"
|
|
|
|
|
#include "dbLayoutToNetlist.h"
|
|
|
|
|
#include "dbDeepRegion.h"
|
|
|
|
|
#include "dbShapeRepository.h"
|
|
|
|
|
#include "dbCellMapping.h"
|
2019-04-20 20:30:12 +02:00
|
|
|
#include "dbLayoutToNetlistWriter.h"
|
|
|
|
|
#include "dbLayoutToNetlistReader.h"
|
2018-12-30 17:53:46 +01:00
|
|
|
|
|
|
|
|
namespace db
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// the iterator provides the hierarchical selection (enabling/disabling cells etc.)
|
2019-01-20 23:12:27 +01:00
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
LayoutToNetlist::LayoutToNetlist (const db::RecursiveShapeIterator &iter)
|
2019-03-03 18:10:52 +01:00
|
|
|
: m_iter (iter), m_layout_index (0), m_netlist_extracted (false), m_is_flat (false)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
|
|
|
|
// check the iterator
|
|
|
|
|
if (iter.has_complex_region () || iter.region () != db::Box::world ()) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist extractor cannot work on clipped layouts")));
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
mp_internal_dss.reset (new db::DeepShapeStore ());
|
|
|
|
|
mp_dss.reset (mp_internal_dss.get ());
|
2019-01-20 23:12:27 +01:00
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
// the dummy layer acts as a reference holder for the layout
|
|
|
|
|
// NOTE: this probably can be done better
|
|
|
|
|
db::RecursiveShapeIterator empty_iter = iter;
|
|
|
|
|
empty_iter.set_layers (std::vector<unsigned int> ());
|
|
|
|
|
m_dummy_layer = dss ().create_polygon_layer (empty_iter);
|
2019-02-28 00:55:06 +01:00
|
|
|
|
|
|
|
|
init ();
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
LayoutToNetlist::LayoutToNetlist (db::DeepShapeStore *dss, unsigned int layout_index)
|
|
|
|
|
: mp_dss (dss), m_layout_index (layout_index), m_netlist_extracted (false), m_is_flat (false)
|
2019-02-28 22:23:20 +01:00
|
|
|
{
|
2019-03-03 18:10:52 +01:00
|
|
|
if (dss->is_valid_layout_index (m_layout_index)) {
|
|
|
|
|
m_iter = db::RecursiveShapeIterator (dss->layout (m_layout_index), dss->initial_cell (m_layout_index), std::set<unsigned int> ());
|
|
|
|
|
}
|
2019-02-28 22:23:20 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-28 00:55:06 +01:00
|
|
|
LayoutToNetlist::LayoutToNetlist (const std::string &topcell_name, double dbu)
|
|
|
|
|
: m_iter (), m_netlist_extracted (false), m_is_flat (true)
|
|
|
|
|
{
|
|
|
|
|
mp_internal_dss.reset (new db::DeepShapeStore (topcell_name, dbu));
|
|
|
|
|
mp_dss.reset (mp_internal_dss.get ());
|
2019-03-03 18:10:52 +01:00
|
|
|
m_layout_index = 0 ;
|
2019-02-28 00:55:06 +01:00
|
|
|
|
|
|
|
|
init ();
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 23:12:27 +01:00
|
|
|
LayoutToNetlist::LayoutToNetlist ()
|
2019-03-03 18:10:52 +01:00
|
|
|
: m_iter (), mp_internal_dss (new db::DeepShapeStore ()), mp_dss (mp_internal_dss.get ()), m_layout_index (0),
|
|
|
|
|
m_netlist_extracted (false), m_is_flat (false)
|
2019-01-20 23:12:27 +01:00
|
|
|
{
|
|
|
|
|
init ();
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 22:23:20 +01:00
|
|
|
LayoutToNetlist::~LayoutToNetlist ()
|
|
|
|
|
{
|
|
|
|
|
// NOTE: do this in this order because of unregistration of the layers
|
|
|
|
|
m_named_regions.clear ();
|
|
|
|
|
m_dlrefs.clear ();
|
|
|
|
|
mp_internal_dss.reset (0);
|
|
|
|
|
mp_netlist.reset (0);
|
|
|
|
|
m_net_clusters.clear ();
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 23:12:27 +01:00
|
|
|
void LayoutToNetlist::init ()
|
|
|
|
|
{
|
2019-02-28 00:55:06 +01:00
|
|
|
dss ().set_text_enlargement (1);
|
|
|
|
|
dss ().set_text_property_name (tl::Variant ("LABEL"));
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LayoutToNetlist::set_threads (int n)
|
|
|
|
|
{
|
2019-02-28 00:55:06 +01:00
|
|
|
dss ().set_threads (n);
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-30 22:37:31 +01:00
|
|
|
int LayoutToNetlist::threads () const
|
|
|
|
|
{
|
2019-02-28 00:55:06 +01:00
|
|
|
return dss ().threads ();
|
2018-12-30 22:37:31 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
void LayoutToNetlist::set_area_ratio (double ar)
|
|
|
|
|
{
|
2019-02-28 00:55:06 +01:00
|
|
|
dss ().set_max_area_ratio (ar);
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-30 22:37:31 +01:00
|
|
|
double LayoutToNetlist::area_ratio () const
|
|
|
|
|
{
|
2019-02-28 00:55:06 +01:00
|
|
|
return dss ().max_area_ratio ();
|
2018-12-30 22:37:31 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
void LayoutToNetlist::set_max_vertex_count (size_t n)
|
|
|
|
|
{
|
2019-02-28 00:55:06 +01:00
|
|
|
dss ().set_max_vertex_count (n);
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-30 22:37:31 +01:00
|
|
|
size_t LayoutToNetlist::max_vertex_count () const
|
|
|
|
|
{
|
2019-02-28 00:55:06 +01:00
|
|
|
return dss ().max_vertex_count ();
|
2018-12-30 22:37:31 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-19 22:19:08 +01:00
|
|
|
db::Region *LayoutToNetlist::make_layer (const std::string &n)
|
|
|
|
|
{
|
|
|
|
|
db::RecursiveShapeIterator si (m_iter);
|
|
|
|
|
si.shape_flags (db::ShapeIterator::Nothing);
|
|
|
|
|
|
2019-02-28 00:55:06 +01:00
|
|
|
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
2019-03-09 19:40:38 +01:00
|
|
|
if (! n.empty ()) {
|
|
|
|
|
register_layer (*region, n);
|
|
|
|
|
}
|
2019-01-20 23:12:27 +01:00
|
|
|
return region.release ();
|
2019-01-19 22:19:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-16 22:45:58 +01:00
|
|
|
db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::string &n)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
|
|
|
|
db::RecursiveShapeIterator si (m_iter);
|
|
|
|
|
si.set_layer (layer_index);
|
|
|
|
|
si.shape_flags (db::ShapeIterator::All);
|
2019-01-16 22:45:58 +01:00
|
|
|
|
2019-02-28 00:55:06 +01:00
|
|
|
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
2019-03-09 19:40:38 +01:00
|
|
|
if (! n.empty ()) {
|
|
|
|
|
register_layer (*region, n);
|
|
|
|
|
}
|
2019-01-20 23:12:27 +01:00
|
|
|
return region.release ();
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-16 22:45:58 +01:00
|
|
|
db::Region *LayoutToNetlist::make_text_layer (unsigned int layer_index, const std::string &n)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
|
|
|
|
db::RecursiveShapeIterator si (m_iter);
|
|
|
|
|
si.set_layer (layer_index);
|
|
|
|
|
si.shape_flags (db::ShapeIterator::Texts);
|
2019-01-16 22:45:58 +01:00
|
|
|
|
2019-02-28 00:55:06 +01:00
|
|
|
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
2019-04-20 20:30:12 +02:00
|
|
|
if (! n.empty ()) {
|
|
|
|
|
register_layer (*region, n);
|
|
|
|
|
}
|
2019-01-20 23:12:27 +01:00
|
|
|
return region.release ();
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-16 22:45:58 +01:00
|
|
|
db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const std::string &n)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
|
|
|
|
db::RecursiveShapeIterator si (m_iter);
|
|
|
|
|
si.set_layer (layer_index);
|
|
|
|
|
si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes);
|
2019-01-16 22:45:58 +01:00
|
|
|
|
2019-02-28 00:55:06 +01:00
|
|
|
std::auto_ptr <db::Region> region (new db::Region (si, dss ()));
|
2019-04-20 20:30:12 +02:00
|
|
|
if (! n.empty ()) {
|
|
|
|
|
register_layer (*region, n);
|
|
|
|
|
}
|
2019-01-20 23:12:27 +01:00
|
|
|
return region.release ();
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::Region *> &layers)
|
|
|
|
|
{
|
|
|
|
|
if (m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
|
|
|
|
}
|
|
|
|
|
if (! mp_netlist.get ()) {
|
|
|
|
|
mp_netlist.reset (new db::Netlist ());
|
|
|
|
|
}
|
2019-03-03 18:10:52 +01:00
|
|
|
extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters);
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LayoutToNetlist::connect (const db::Region &l)
|
|
|
|
|
{
|
|
|
|
|
if (m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
|
|
|
|
}
|
2019-02-28 22:23:20 +01:00
|
|
|
|
2019-01-20 23:12:27 +01:00
|
|
|
if (! is_persisted (l)) {
|
2019-04-20 20:30:12 +02:00
|
|
|
register_layer (l, make_new_name ());
|
2019-01-20 23:12:27 +01:00
|
|
|
}
|
2018-12-30 17:53:46 +01:00
|
|
|
|
|
|
|
|
// we need to keep a reference, so we can safely delete the region
|
2019-02-28 22:23:20 +01:00
|
|
|
db::DeepLayer dl = deep_layer_of (l);
|
2018-12-30 17:53:46 +01:00
|
|
|
m_dlrefs.insert (dl);
|
|
|
|
|
|
|
|
|
|
m_conn.connect (dl.layer ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LayoutToNetlist::connect (const db::Region &a, const db::Region &b)
|
|
|
|
|
{
|
|
|
|
|
if (m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
|
|
|
|
}
|
2019-01-20 23:12:27 +01:00
|
|
|
if (! is_persisted (a)) {
|
2019-04-20 20:30:12 +02:00
|
|
|
register_layer (a, make_new_name ());
|
2019-01-20 23:12:27 +01:00
|
|
|
}
|
|
|
|
|
if (! is_persisted (b)) {
|
2019-04-20 20:30:12 +02:00
|
|
|
register_layer (b, make_new_name ());
|
2019-01-20 23:12:27 +01:00
|
|
|
}
|
2018-12-30 17:53:46 +01:00
|
|
|
|
|
|
|
|
// we need to keep a reference, so we can safely delete the region
|
2019-02-28 22:23:20 +01:00
|
|
|
db::DeepLayer dla = deep_layer_of (a);
|
|
|
|
|
db::DeepLayer dlb = deep_layer_of (b);
|
2018-12-30 17:53:46 +01:00
|
|
|
m_dlrefs.insert (dla);
|
|
|
|
|
m_dlrefs.insert (dlb);
|
|
|
|
|
|
|
|
|
|
m_conn.connect (dla.layer (), dlb.layer ());
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-06 15:28:40 +01:00
|
|
|
size_t LayoutToNetlist::connect_global (const db::Region &l, const std::string &gn)
|
|
|
|
|
{
|
|
|
|
|
if (m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
|
|
|
|
}
|
2019-01-20 23:12:27 +01:00
|
|
|
if (! is_persisted (l)) {
|
2019-04-20 20:30:12 +02:00
|
|
|
register_layer (l, make_new_name ());
|
2019-01-06 15:28:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we need to keep a reference, so we can safely delete the region
|
2019-02-28 22:23:20 +01:00
|
|
|
db::DeepLayer dl = deep_layer_of (l);
|
2019-01-06 15:28:40 +01:00
|
|
|
m_dlrefs.insert (dl);
|
|
|
|
|
|
|
|
|
|
return m_conn.connect_global (dl.layer (), gn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::string &LayoutToNetlist::global_net_name (size_t id) const
|
|
|
|
|
{
|
|
|
|
|
return m_conn.global_net_name (id);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-07 02:08:59 +01:00
|
|
|
size_t LayoutToNetlist::global_net_id (const std::string &name)
|
|
|
|
|
{
|
|
|
|
|
return m_conn.global_net_id (name);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-15 23:24:27 +02:00
|
|
|
void LayoutToNetlist::extract_netlist (const std::string &joined_net_names)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
|
|
|
|
if (m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
|
|
|
|
}
|
|
|
|
|
if (! mp_netlist.get ()) {
|
|
|
|
|
mp_netlist.reset (new db::Netlist ());
|
|
|
|
|
}
|
2019-01-14 00:59:47 +01:00
|
|
|
|
|
|
|
|
db::NetlistExtractor netex;
|
2019-04-15 23:24:27 +02:00
|
|
|
netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, joined_net_names);
|
2019-01-14 00:59:47 +01:00
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
m_netlist_extracted = true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
void LayoutToNetlist::set_netlist_extracted ()
|
|
|
|
|
{
|
|
|
|
|
m_netlist_extracted = true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
const db::Layout *LayoutToNetlist::internal_layout () const
|
|
|
|
|
{
|
2019-03-03 18:10:52 +01:00
|
|
|
ensure_layout ();
|
|
|
|
|
return &dss ().const_layout (m_layout_index);
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const db::Cell *LayoutToNetlist::internal_top_cell () const
|
|
|
|
|
{
|
2019-03-03 18:10:52 +01:00
|
|
|
ensure_layout ();
|
|
|
|
|
return &dss ().const_initial_cell (m_layout_index);
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
db::Layout *LayoutToNetlist::internal_layout ()
|
2019-01-20 23:12:27 +01:00
|
|
|
{
|
2019-03-03 18:10:52 +01:00
|
|
|
ensure_layout ();
|
|
|
|
|
return &dss ().layout (m_layout_index);
|
2019-01-20 23:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
db::Cell *LayoutToNetlist::internal_top_cell ()
|
2019-01-19 22:19:08 +01:00
|
|
|
{
|
2019-03-03 18:10:52 +01:00
|
|
|
ensure_layout ();
|
|
|
|
|
return &dss ().initial_cell (m_layout_index);
|
2019-01-19 22:19:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
void LayoutToNetlist::ensure_layout () const
|
2019-01-19 22:19:08 +01:00
|
|
|
{
|
2019-03-03 18:10:52 +01:00
|
|
|
if (! dss ().is_valid_layout_index (m_layout_index)) {
|
|
|
|
|
|
|
|
|
|
LayoutToNetlist *non_const_this = const_cast<LayoutToNetlist *> (this);
|
|
|
|
|
non_const_this->dss ().make_layout (m_layout_index, db::RecursiveShapeIterator ());
|
|
|
|
|
|
|
|
|
|
// the dummy layer acts as a reference holder for the layout
|
|
|
|
|
unsigned int dummy_layer_index = non_const_this->dss ().layout (m_layout_index).insert_layer ();
|
|
|
|
|
non_const_this->m_dummy_layer = db::DeepLayer (& non_const_this->dss (), m_layout_index, dummy_layer_index);
|
|
|
|
|
|
|
|
|
|
}
|
2019-01-19 22:19:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-20 23:12:27 +01:00
|
|
|
void LayoutToNetlist::register_layer (const db::Region ®ion, const std::string &n)
|
|
|
|
|
{
|
|
|
|
|
if (m_named_regions.find (n) != m_named_regions.end ()) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("Layer name is already used: ")) + n);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 00:55:06 +01:00
|
|
|
db::DeepLayer dl;
|
|
|
|
|
|
|
|
|
|
if (m_is_flat) {
|
|
|
|
|
|
2019-03-10 19:35:13 +01:00
|
|
|
dl = dss ().create_from_flat (region, true);
|
2019-02-28 00:55:06 +01:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
db::DeepRegion *delegate = dynamic_cast<db::DeepRegion *> (region.delegate());
|
|
|
|
|
if (! delegate) {
|
|
|
|
|
|
|
|
|
|
if (region.empty ()) {
|
2019-03-03 18:10:52 +01:00
|
|
|
dl = dss ().empty_layer (m_layout_index);
|
2019-02-28 00:55:06 +01:00
|
|
|
} else {
|
2019-03-13 16:14:27 +01:00
|
|
|
dl = dss ().create_from_flat (region, true);
|
2019-02-28 00:55:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (is_persisted (region)) {
|
|
|
|
|
std::string prev_name = name (region);
|
|
|
|
|
m_named_regions.erase (prev_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dl = delegate->deep_layer ();
|
|
|
|
|
|
|
|
|
|
}
|
2019-01-20 23:12:27 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 00:55:06 +01:00
|
|
|
m_named_regions [n] = dl;
|
|
|
|
|
m_name_of_layer [dl.layer ()] = n;
|
2019-01-20 23:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-20 20:30:12 +02:00
|
|
|
std::string LayoutToNetlist::make_new_name (const std::string &stem)
|
|
|
|
|
{
|
|
|
|
|
int m = std::numeric_limits<int>::max () / 2 + 1;
|
|
|
|
|
int n = m;
|
|
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
|
while (m > 0) {
|
|
|
|
|
|
|
|
|
|
m /= 2;
|
|
|
|
|
|
|
|
|
|
name = stem;
|
|
|
|
|
name += std::string ("$");
|
|
|
|
|
name += tl::to_string (n - m);
|
|
|
|
|
|
|
|
|
|
if (m_named_regions.find (name) == m_named_regions.end ()) {
|
|
|
|
|
n -= m;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 23:12:27 +01:00
|
|
|
std::string LayoutToNetlist::name (const db::Region ®ion) const
|
|
|
|
|
{
|
|
|
|
|
std::map<unsigned int, std::string>::const_iterator n = m_name_of_layer.find (layer_of (region));
|
|
|
|
|
if (n != m_name_of_layer.end ()) {
|
|
|
|
|
return n->second;
|
|
|
|
|
} else {
|
|
|
|
|
return std::string ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string LayoutToNetlist::name (unsigned int l) const
|
|
|
|
|
{
|
|
|
|
|
std::map<unsigned int, std::string>::const_iterator n = m_name_of_layer.find (l);
|
|
|
|
|
if (n != m_name_of_layer.end ()) {
|
|
|
|
|
return n->second;
|
|
|
|
|
} else {
|
|
|
|
|
return std::string ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool LayoutToNetlist::is_persisted (const db::Region ®ion) const
|
2019-01-16 22:45:58 +01:00
|
|
|
{
|
2019-01-20 23:12:27 +01:00
|
|
|
return m_name_of_layer.find (layer_of (region)) != m_name_of_layer.end ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::Region *LayoutToNetlist::layer_by_name (const std::string &name)
|
|
|
|
|
{
|
|
|
|
|
std::map<std::string, db::DeepLayer>::const_iterator l = m_named_regions.find (name);
|
|
|
|
|
if (l == m_named_regions.end ()) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return new db::Region (new db::DeepRegion (l->second));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::Region *LayoutToNetlist::layer_by_index (unsigned int index)
|
|
|
|
|
{
|
|
|
|
|
std::map<unsigned int, std::string>::const_iterator n = m_name_of_layer.find (index);
|
|
|
|
|
if (n == m_name_of_layer.end ()) {
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
return layer_by_name (n->second);
|
|
|
|
|
}
|
2019-01-16 22:45:58 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-28 22:23:20 +01:00
|
|
|
db::DeepLayer LayoutToNetlist::deep_layer_of (const db::Region ®ion) const
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
|
|
|
|
const db::DeepRegion *dr = dynamic_cast<const db::DeepRegion *> (region.delegate ());
|
|
|
|
|
if (! dr) {
|
2019-02-28 22:23:20 +01:00
|
|
|
|
|
|
|
|
std::pair<bool, db::DeepLayer> lff = dss ().layer_for_flat (region);
|
|
|
|
|
if (lff.first) {
|
|
|
|
|
return lff.second;
|
|
|
|
|
} else if (region.empty ()) {
|
|
|
|
|
// provide a substitute empty layer for empty
|
2019-03-03 18:10:52 +01:00
|
|
|
return dss ().empty_layer (m_layout_index);
|
2019-02-28 22:23:20 +01:00
|
|
|
} else {
|
|
|
|
|
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in netlist extraction"))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
return dr->deep_layer ();
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
2019-02-28 22:23:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int LayoutToNetlist::layer_of (const db::Region ®ion) const
|
|
|
|
|
{
|
|
|
|
|
return deep_layer_of (region).layer ();
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-15 21:33:41 +01:00
|
|
|
db::CellMapping LayoutToNetlist::cell_mapping_into (db::Layout &layout, db::Cell &cell, bool with_device_cells)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
2019-01-15 21:33:41 +01:00
|
|
|
std::set<db::cell_index_type> device_cells;
|
2019-01-19 23:00:19 +01:00
|
|
|
if (! with_device_cells && mp_netlist.get ()) {
|
2019-01-21 22:37:13 +01:00
|
|
|
for (db::Netlist::device_abstract_iterator i = mp_netlist->begin_device_abstracts (); i != mp_netlist->end_device_abstracts (); ++i) {
|
2019-01-19 23:00:19 +01:00
|
|
|
device_cells.insert (i->cell_index ());
|
2019-01-15 21:33:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
return dss ().cell_mapping_to_original (m_layout_index, &layout, cell.cell_index (), &device_cells);
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layout, const db::Cell &cell)
|
|
|
|
|
{
|
|
|
|
|
db::CellMapping cm;
|
|
|
|
|
if (layout.cells () == 1) {
|
|
|
|
|
cm.create_single_mapping (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ());
|
|
|
|
|
} else {
|
|
|
|
|
cm.create_from_geometry (layout, cell.cell_index (), *internal_layout(), internal_top_cell()->cell_index ());
|
|
|
|
|
}
|
|
|
|
|
return cm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::Netlist *LayoutToNetlist::netlist () const
|
|
|
|
|
{
|
|
|
|
|
return mp_netlist.get ();
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-19 22:19:08 +01:00
|
|
|
db::Netlist *LayoutToNetlist::make_netlist ()
|
|
|
|
|
{
|
|
|
|
|
if (! mp_netlist.get ()) {
|
|
|
|
|
mp_netlist.reset (new db::Netlist ());
|
|
|
|
|
}
|
|
|
|
|
return mp_netlist.get ();
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
struct StopOnFirst { };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class Tr>
|
2019-05-03 23:33:37 +02:00
|
|
|
static bool deliver_shape (const db::PolygonRef &, StopOnFirst, const Tr &, db::properties_id_type)
|
2019-01-20 02:50:23 +01:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-01-19 22:19:08 +01:00
|
|
|
|
2019-01-04 08:03:31 +01:00
|
|
|
template <class Tr>
|
2019-05-03 23:33:37 +02:00
|
|
|
static bool deliver_shape (const db::PolygonRef &pr, db::Region ®ion, const Tr &tr, db::properties_id_type /*propid*/)
|
2019-01-04 08:03:31 +01:00
|
|
|
{
|
|
|
|
|
if (pr.obj ().is_box ()) {
|
|
|
|
|
region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr));
|
|
|
|
|
} else {
|
|
|
|
|
region.insert (pr.obj ().transformed (pr.trans ()).transformed (tr));
|
|
|
|
|
}
|
2019-01-20 02:50:23 +01:00
|
|
|
return true;
|
2019-01-04 08:03:31 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-04 17:41:09 +01:00
|
|
|
template <class Tr>
|
2019-05-03 23:33:37 +02:00
|
|
|
static bool deliver_shape (const db::PolygonRef &pr, db::Shapes &shapes, const Tr &tr, db::properties_id_type propid)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
2019-01-04 17:41:09 +01:00
|
|
|
if (pr.obj ().is_box ()) {
|
2019-05-03 23:33:37 +02:00
|
|
|
if (propid) {
|
|
|
|
|
shapes.insert (db::BoxWithProperties (pr.obj ().box ().transformed (pr.trans ()).transformed (tr), propid));
|
|
|
|
|
} else {
|
|
|
|
|
shapes.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr));
|
|
|
|
|
}
|
2019-01-04 17:41:09 +01:00
|
|
|
} else {
|
|
|
|
|
db::Layout *layout = shapes.layout ();
|
|
|
|
|
if (layout) {
|
2019-05-03 23:33:37 +02:00
|
|
|
db::PolygonRef polygon_ref (pr.obj ().transformed (pr.trans ()).transformed (tr), layout->shape_repository ());
|
|
|
|
|
if (propid) {
|
|
|
|
|
shapes.insert (db::PolygonRefWithProperties (polygon_ref, propid));
|
|
|
|
|
} else {
|
|
|
|
|
shapes.insert (polygon_ref);
|
|
|
|
|
}
|
2019-01-04 17:41:09 +01:00
|
|
|
} else {
|
2019-05-03 23:33:37 +02:00
|
|
|
db::Polygon polygon (pr.obj ().transformed (pr.trans ()).transformed (tr));
|
|
|
|
|
if (propid) {
|
|
|
|
|
shapes.insert (db::PolygonWithProperties (polygon, propid));
|
|
|
|
|
} else {
|
|
|
|
|
shapes.insert (polygon);
|
|
|
|
|
}
|
2019-01-04 17:41:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-01-20 02:50:23 +01:00
|
|
|
return true;
|
2019-01-04 17:41:09 +01:00
|
|
|
}
|
2018-12-30 17:53:46 +01:00
|
|
|
|
2019-01-04 17:41:09 +01:00
|
|
|
template <class To>
|
2019-05-03 23:33:37 +02:00
|
|
|
static bool deliver_shapes_of_net_recursive (const db::Netlist * /*nl*/, const db::hier_clusters<db::PolygonRef> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to, db::properties_id_type propid)
|
2019-01-04 17:41:09 +01:00
|
|
|
{
|
2019-01-15 21:33:41 +01:00
|
|
|
// deliver the net shapes
|
|
|
|
|
for (db::recursive_cluster_shape_iterator<db::PolygonRef> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
|
2019-05-03 23:33:37 +02:00
|
|
|
if (! deliver_shape (*rci, to, tr * rci.trans (), propid)) {
|
2019-01-20 02:50:23 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-01-04 17:41:09 +01:00
|
|
|
}
|
2019-01-20 02:50:23 +01:00
|
|
|
return true;
|
2019-01-04 17:41:09 +01:00
|
|
|
}
|
2018-12-30 17:53:46 +01:00
|
|
|
|
2019-01-04 17:41:09 +01:00
|
|
|
template <class To>
|
2019-05-03 23:33:37 +02:00
|
|
|
static bool deliver_shapes_of_net_nonrecursive (const db::Netlist *nl, const db::hier_clusters<db::PolygonRef> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to, db::properties_id_type propid)
|
2019-01-04 17:41:09 +01:00
|
|
|
{
|
2019-01-20 02:50:23 +01:00
|
|
|
// NOTE: this scheme will deliver the shapes from the cell, including (!)
|
|
|
|
|
// subcells that are purged
|
|
|
|
|
|
|
|
|
|
db::cell_index_type prev_ci = ci;
|
|
|
|
|
|
|
|
|
|
// deliver the net shapes
|
|
|
|
|
for (db::recursive_cluster_shape_iterator<db::PolygonRef> rci (clusters, layer_id, ci, cid); !rci.at_end (); ) {
|
|
|
|
|
|
|
|
|
|
db::cell_index_type cci = rci.cell_index ();
|
2019-01-21 22:37:13 +01:00
|
|
|
if (cci != prev_ci && cci != ci && (! nl || nl->circuit_by_cell_index (cci) || nl->device_abstract_by_cell_index (cci))) {
|
2019-01-20 02:50:23 +01:00
|
|
|
|
|
|
|
|
rci.skip_cell ();
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
if (! deliver_shape (*rci, to, tr * rci.trans (), propid)) {
|
2019-01-20 02:50:23 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
prev_ci = cci;
|
|
|
|
|
|
|
|
|
|
++rci;
|
|
|
|
|
|
|
|
|
|
}
|
2018-12-30 17:53:46 +01:00
|
|
|
|
2019-01-04 17:41:09 +01:00
|
|
|
}
|
2019-01-20 02:50:23 +01:00
|
|
|
|
|
|
|
|
return true;
|
2019-01-04 17:41:09 +01:00
|
|
|
}
|
2018-12-30 17:53:46 +01:00
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
template <class To>
|
|
|
|
|
static bool deliver_shapes_of_net (bool recursive, const db::Netlist *nl, const db::hier_clusters<db::PolygonRef> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, const db::ICplxTrans &tr, To &to, db::properties_id_type propid)
|
|
|
|
|
{
|
|
|
|
|
if (recursive) {
|
|
|
|
|
return deliver_shapes_of_net_recursive (nl, clusters, ci, cid, layer_id, tr, to, propid);
|
|
|
|
|
} else {
|
|
|
|
|
return deliver_shapes_of_net_nonrecursive (nl, clusters, ci, cid, layer_id, tr, to, propid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid) const
|
2019-01-04 17:41:09 +01:00
|
|
|
{
|
|
|
|
|
unsigned int lid = layer_of (of_layer);
|
2019-01-15 21:33:41 +01:00
|
|
|
const db::Circuit *circuit = net.circuit ();
|
|
|
|
|
tl_assert (circuit != 0);
|
2018-12-30 17:53:46 +01:00
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), to, propid);
|
2019-01-04 17:41:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive) const
|
|
|
|
|
{
|
|
|
|
|
unsigned int lid = layer_of (of_layer);
|
2019-01-15 21:33:41 +01:00
|
|
|
const db::Circuit *circuit = net.circuit ();
|
|
|
|
|
tl_assert (circuit != 0);
|
|
|
|
|
|
2019-01-04 17:41:09 +01:00
|
|
|
std::auto_ptr<db::Region> res (new db::Region ());
|
2018-12-30 17:53:46 +01:00
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lid, db::ICplxTrans (), *res, 0);
|
2018-12-30 17:53:46 +01:00
|
|
|
|
2019-01-02 23:23:04 +01:00
|
|
|
return res.release ();
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-06 01:32:20 +01:00
|
|
|
void
|
2019-05-04 00:37:38 +02:00
|
|
|
LayoutToNetlist::build_net_rec (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const db::ICplxTrans &tr) const
|
2019-01-15 21:33:41 +01:00
|
|
|
{
|
|
|
|
|
const db::Circuit *circuit = net.circuit ();
|
|
|
|
|
tl_assert (circuit != 0);
|
|
|
|
|
|
2019-05-04 00:37:38 +02:00
|
|
|
build_net_rec (circuit->cell_index (), net.cluster_id (), target, target_cell, lmap, &net, net_cell_name_prefix, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, reuse_table, tr);
|
2019-01-15 21:33:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-05-04 00:37:38 +02:00
|
|
|
LayoutToNetlist::build_net_rec (db::cell_index_type ci, size_t cid, db::Layout &target, db::Cell &tc, const std::map<unsigned int, const db::Region *> &lmap, const db::Net *net, const char *net_cell_name_prefix, db::properties_id_type netname_propid, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix, cell_reuse_table_type &reuse_table, const db::ICplxTrans &tr) const
|
2019-01-06 01:32:20 +01:00
|
|
|
{
|
2019-01-20 02:50:23 +01:00
|
|
|
db::Cell *target_cell = &tc;
|
|
|
|
|
|
|
|
|
|
if (net_cell_name_prefix) {
|
|
|
|
|
|
|
|
|
|
const db::connected_clusters<db::PolygonRef> &ccl = m_net_clusters.clusters_per_cell (ci);
|
|
|
|
|
|
|
|
|
|
bool any_connections = circuit_cell_name_prefix && ! ccl.connections_for_cluster (cid).empty ();
|
|
|
|
|
if (! any_connections) {
|
|
|
|
|
|
|
|
|
|
bool consider_cell = any_connections;
|
|
|
|
|
for (std::map<unsigned int, const db::Region *>::const_iterator l = lmap.begin (); l != lmap.end () && !consider_cell; ++l) {
|
2019-01-20 23:12:27 +01:00
|
|
|
if (l->second) {
|
|
|
|
|
StopOnFirst sof;
|
2019-05-03 23:33:37 +02:00
|
|
|
consider_cell = !deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, sof, 0);
|
2019-01-20 23:12:27 +01:00
|
|
|
}
|
2019-01-20 02:50:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! consider_cell) {
|
|
|
|
|
// shortcut if cell is empty -> no net cell will be produced
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make a specific cell for the net if requested
|
|
|
|
|
|
|
|
|
|
target_cell = &target.cell (target.add_cell ((std::string (net_cell_name_prefix) + net->expanded_name ()).c_str ()));
|
|
|
|
|
tc.insert (db::CellInstArray (db::CellInst (target_cell->cell_index ()), db::Trans ()));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-06 01:32:20 +01:00
|
|
|
for (std::map<unsigned int, const db::Region *>::const_iterator l = lmap.begin (); l != lmap.end (); ++l) {
|
2019-01-20 23:12:27 +01:00
|
|
|
if (l->second) {
|
2019-05-03 23:33:37 +02:00
|
|
|
deliver_shapes_of_net (hier_mode == BNH_Flatten, mp_netlist.get (), m_net_clusters, ci, cid, layer_of (*l->second), tr, target_cell->shapes (l->first), netname_propid);
|
2019-01-20 23:12:27 +01:00
|
|
|
}
|
2019-01-06 01:32:20 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
if (hier_mode != BNH_SubcircuitCells && ! device_cell_name_prefix) {
|
2019-01-06 01:32:20 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 23:50:34 +01:00
|
|
|
// NOTE: we propagate the magnification part of tr down, but keep the rotation/translation part in the instance
|
|
|
|
|
// (we want to avoid magnified instances)
|
|
|
|
|
db::ICplxTrans tr_wo_mag = tr * db::ICplxTrans (1.0 / tr.mag ());
|
|
|
|
|
db::ICplxTrans tr_mag (tr.mag ());
|
|
|
|
|
|
2019-01-15 21:33:41 +01:00
|
|
|
const db::connected_clusters<db::PolygonRef> &clusters = m_net_clusters.clusters_per_cell (ci);
|
2019-01-06 01:32:20 +01:00
|
|
|
typedef db::connected_clusters<db::PolygonRef>::connections_type connections_type;
|
2019-01-15 21:33:41 +01:00
|
|
|
const connections_type &connections = clusters.connections_for_cluster (cid);
|
2019-01-06 01:32:20 +01:00
|
|
|
for (connections_type::const_iterator c = connections.begin (); c != connections.end (); ++c) {
|
|
|
|
|
|
2019-02-25 22:31:54 +01:00
|
|
|
db::cell_index_type subci = c->inst_cell_index ();
|
2019-01-15 21:33:41 +01:00
|
|
|
size_t subcid = c->id ();
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-05-04 00:37:38 +02:00
|
|
|
CellReuseTableKey cmap_key (subci, netname_propid, subcid);
|
|
|
|
|
|
|
|
|
|
cell_reuse_table_type::const_iterator cm = reuse_table.find (cmap_key);
|
|
|
|
|
if (cm == reuse_table.end ()) {
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-15 21:33:41 +01:00
|
|
|
const char *name_prefix = 0;
|
2019-01-21 22:37:13 +01:00
|
|
|
if (mp_netlist->device_abstract_by_cell_index (subci)) {
|
2019-01-15 21:33:41 +01:00
|
|
|
name_prefix = device_cell_name_prefix;
|
|
|
|
|
} else {
|
2019-01-20 02:50:23 +01:00
|
|
|
name_prefix = circuit_cell_name_prefix;
|
2019-01-15 21:33:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (name_prefix) {
|
|
|
|
|
|
|
|
|
|
std::string cell_name = internal_layout ()->cell_name (subci);
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-15 21:33:41 +01:00
|
|
|
db::cell_index_type target_ci = target.add_cell ((std::string (name_prefix) + cell_name).c_str ());
|
2019-05-04 00:37:38 +02:00
|
|
|
cm = reuse_table.insert (std::make_pair (cmap_key, target_ci)).first;
|
2019-01-15 21:33:41 +01:00
|
|
|
|
2019-05-04 00:37:38 +02:00
|
|
|
build_net_rec (subci, subcid, target, target.cell (target_ci), lmap, 0, 0, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, reuse_table, tr_mag);
|
2019-01-15 21:33:41 +01:00
|
|
|
|
|
|
|
|
} else {
|
2019-05-04 00:37:38 +02:00
|
|
|
cm = reuse_table.insert (std::make_pair (cmap_key, std::numeric_limits<db::cell_index_type>::max ())).first;
|
2019-01-15 21:33:41 +01:00
|
|
|
}
|
2019-01-06 01:32:20 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-15 21:33:41 +01:00
|
|
|
if (cm->second != std::numeric_limits<db::cell_index_type>::max ()) {
|
2019-02-25 22:31:54 +01:00
|
|
|
db::CellInstArray ci (db::CellInst (cm->second), tr_wo_mag * c->inst_trans ());
|
2019-01-31 23:50:34 +01:00
|
|
|
ci.transform_into (tr_mag);
|
|
|
|
|
target_cell->insert (ci);
|
2019-01-15 21:33:41 +01:00
|
|
|
}
|
2019-01-06 01:32:20 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
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 ()) {
|
|
|
|
|
|
|
|
|
|
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 ())));
|
|
|
|
|
|
|
|
|
|
return ly.properties_repository ().properties_id (propset);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-06 01:32:20 +01:00
|
|
|
void
|
2019-05-03 23:33:37 +02:00
|
|
|
LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map<unsigned int, const db::Region *> &lmap, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const
|
2019-01-06 01:32:20 +01:00
|
|
|
{
|
|
|
|
|
if (! m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-04 00:37:38 +02:00
|
|
|
cell_reuse_table_type cell_reuse_table;
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-31 23:50:34 +01:00
|
|
|
double mag = internal_layout ()->dbu () / target.dbu ();
|
2019-05-03 23:33:37 +02:00
|
|
|
|
|
|
|
|
db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, net);
|
2019-05-04 00:37:38 +02:00
|
|
|
build_net_rec (net, target, target_cell, lmap, 0, netname_propid, hier_mode, cell_name_prefix, device_cell_name_prefix, cell_reuse_table, db::ICplxTrans (mag));
|
2019-01-06 01:32:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-05-03 23:33:37 +02:00
|
|
|
LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const
|
2019-05-04 18:48:57 +02:00
|
|
|
{
|
|
|
|
|
build_nets (0, cmap, target, lmap, net_cell_name_prefix, netname_prop, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
LayoutToNetlist::build_nets (const std::set<const db::Net *> *nets, const db::CellMapping &cmap, db::Layout &target, const std::map<unsigned int, const db::Region *> &lmap, const char *net_cell_name_prefix, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const
|
2019-01-06 01:32:20 +01:00
|
|
|
{
|
|
|
|
|
if (! m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-04 00:37:38 +02:00
|
|
|
cell_reuse_table_type cell_reuse_table;
|
2019-01-31 23:50:34 +01:00
|
|
|
double mag = internal_layout ()->dbu () / target.dbu ();
|
2019-01-06 01:54:36 +01:00
|
|
|
|
2019-01-06 01:32:20 +01:00
|
|
|
const db::Netlist *netlist = mp_netlist.get ();
|
|
|
|
|
for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) {
|
|
|
|
|
|
|
|
|
|
if (! cmap.has_mapping (c->cell_index ())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
bool is_top_circuit = c->begin_parents () == c->end_parents ();
|
|
|
|
|
|
2019-01-06 01:32:20 +01:00
|
|
|
db::cell_index_type target_ci = cmap.cell_mapping (c->cell_index ());
|
|
|
|
|
|
|
|
|
|
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
|
|
|
|
|
|
2019-05-04 18:48:57 +02:00
|
|
|
// exlude local nets in recursive mode except if they are explicitly selected
|
|
|
|
|
if (! nets && hier_mode != BNH_Disconnected && ! is_top_circuit && n->pin_count () > 0) {
|
2019-01-06 12:53:22 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-05-04 18:48:57 +02:00
|
|
|
if (! nets || nets->find (n.operator-> ()) != nets->end ()) {
|
|
|
|
|
db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n);
|
|
|
|
|
build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, db::ICplxTrans (mag));
|
|
|
|
|
}
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
}
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-05-04 18:48:57 +02:00
|
|
|
if (hier_mode != BNH_Disconnected && ! nets) {
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-05-04 18:48:57 +02:00
|
|
|
// With recursive nets we skip nets in subcircuits which are connected upwards. This means, nets will
|
2019-01-20 02:50:23 +01:00
|
|
|
// get lost if there is no connection to this pin from the outside. Hence we need to deliver nets from
|
|
|
|
|
// subcircuits as part of the circuit which calls the subcircuit - but NOT in a subcircuit cell, because
|
|
|
|
|
// this will just apply to nets from certain instances. But the net cell name will be formed as "subcircuit:net"
|
2019-05-04 18:48:57 +02:00
|
|
|
//
|
|
|
|
|
// In explicit selection mode we don't care about this as nets are explicitly taken or not.
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
const db::Circuit &circuit = *c;
|
|
|
|
|
for (db::Circuit::const_subcircuit_iterator sc = circuit.begin_subcircuits (); sc != circuit.end_subcircuits (); ++sc) {
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
const db::SubCircuit &subcircuit = *sc;
|
|
|
|
|
for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) {
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
if (! subcircuit.net_for_pin (p->id ())) {
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
const db::Net *n = subcircuit.circuit_ref ()->net_for_pin (p->id ());
|
|
|
|
|
if (n) {
|
2019-01-06 01:32:20 +01:00
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
double dbu = target.dbu ();
|
2019-01-31 23:50:34 +01:00
|
|
|
db::ICplxTrans tr = db::ICplxTrans (mag) * (db::CplxTrans (dbu).inverted () * subcircuit.trans () * db::CplxTrans (dbu));
|
2019-01-20 02:50:23 +01:00
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
db::properties_id_type netname_propid = make_netname_propid (target, netname_prop, *n);
|
|
|
|
|
|
2019-01-20 02:50:23 +01:00
|
|
|
if (net_cell_name_prefix) {
|
|
|
|
|
std::string ncn = std::string (net_cell_name_prefix) + subcircuit.expanded_name () + ":";
|
2019-05-04 00:37:38 +02:00
|
|
|
build_net_rec (*n, target, target.cell (target_ci), lmap, ncn.c_str (), netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, tr);
|
2019-01-20 02:50:23 +01:00
|
|
|
} else {
|
2019-05-04 00:37:38 +02:00
|
|
|
build_net_rec (*n, target, target.cell (target_ci), lmap, net_cell_name_prefix, netname_propid, hier_mode, circuit_cell_name_prefix, device_cell_name_prefix, cell_reuse_table, tr);
|
2019-01-20 02:50:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2019-01-06 12:53:22 +01:00
|
|
|
|
2019-01-06 01:32:20 +01:00
|
|
|
}
|
2019-01-06 12:53:22 +01:00
|
|
|
|
2019-01-06 01:32:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::DPoint &point)
|
|
|
|
|
{
|
|
|
|
|
return probe_net (of_region, db::CplxTrans (internal_layout ()->dbu ()).inverted () * point);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-30 22:37:31 +01:00
|
|
|
size_t LayoutToNetlist::search_net (const db::ICplxTrans &trans, const db::Cell *cell, const db::local_cluster<db::PolygonRef> &test_cluster, std::vector<db::InstElement> &rev_inst_path)
|
2018-12-30 17:53:46 +01:00
|
|
|
{
|
2018-12-30 22:37:31 +01:00
|
|
|
db::Box local_box = trans * test_cluster.bbox ();
|
2018-12-30 17:53:46 +01:00
|
|
|
|
|
|
|
|
const db::local_clusters<db::PolygonRef> &lcc = net_clusters ().clusters_per_cell (cell->cell_index ());
|
|
|
|
|
for (db::local_clusters<db::PolygonRef>::touching_iterator i = lcc.begin_touching (local_box); ! i.at_end (); ++i) {
|
|
|
|
|
const db::local_cluster<db::PolygonRef> &lc = *i;
|
|
|
|
|
if (lc.interacts (test_cluster, trans, m_conn)) {
|
|
|
|
|
return lc.id ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (db::Cell::touching_iterator i = cell->begin_touching (local_box); ! i.at_end (); ++i) {
|
2018-12-30 22:37:31 +01:00
|
|
|
|
|
|
|
|
for (db::CellInstArray::iterator ia = i->begin_touching (local_box, internal_layout ()); ! ia.at_end (); ++ia) {
|
|
|
|
|
|
|
|
|
|
db::ICplxTrans trans_inst = i->complex_trans (*ia);
|
|
|
|
|
db::ICplxTrans t = trans_inst.inverted () * trans;
|
|
|
|
|
size_t cluster_id = search_net (t, &internal_layout ()->cell (i->cell_index ()), test_cluster, rev_inst_path);
|
|
|
|
|
if (cluster_id > 0) {
|
|
|
|
|
rev_inst_path.push_back (db::InstElement (*i, ia));
|
|
|
|
|
return cluster_id;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
2018-12-30 22:37:31 +01:00
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::Net *LayoutToNetlist::probe_net (const db::Region &of_region, const db::Point &point)
|
|
|
|
|
{
|
|
|
|
|
if (! m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
|
|
|
|
}
|
|
|
|
|
tl_assert (mp_netlist.get ());
|
|
|
|
|
|
2018-12-30 22:37:31 +01:00
|
|
|
db::CplxTrans dbu_trans (internal_layout ()->dbu ());
|
|
|
|
|
db::VCplxTrans dbu_trans_inv = dbu_trans.inverted ();
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
unsigned int layer = layer_of (of_region);
|
|
|
|
|
|
|
|
|
|
// Prepare a test cluster
|
|
|
|
|
db::Box box (point - db::Vector (1, 1), point + db::Vector (1, 1));
|
|
|
|
|
db::GenericRepository sr;
|
|
|
|
|
db::local_cluster<db::PolygonRef> test_cluster;
|
|
|
|
|
test_cluster.add (db::PolygonRef (db::Polygon (box), sr), layer);
|
|
|
|
|
|
2018-12-30 22:37:31 +01:00
|
|
|
std::vector<db::InstElement> inst_path;
|
|
|
|
|
|
|
|
|
|
size_t cluster_id = search_net (db::ICplxTrans (), internal_top_cell (), test_cluster, inst_path);
|
2018-12-30 17:53:46 +01:00
|
|
|
if (cluster_id > 0) {
|
|
|
|
|
|
2018-12-30 22:37:31 +01:00
|
|
|
// search_net delivers the path in reverse order
|
|
|
|
|
std::reverse (inst_path.begin (), inst_path.end ());
|
|
|
|
|
|
|
|
|
|
std::vector<db::cell_index_type> cell_indexes;
|
|
|
|
|
cell_indexes.reserve (inst_path.size () + 1);
|
|
|
|
|
cell_indexes.push_back (internal_top_cell ()->cell_index ());
|
|
|
|
|
for (std::vector<db::InstElement>::const_iterator i = inst_path.begin (); i != inst_path.end (); ++i) {
|
|
|
|
|
cell_indexes.push_back (i->inst_ptr.cell_index ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::Circuit *circuit = mp_netlist->circuit_by_cell_index (cell_indexes.back ());
|
|
|
|
|
if (! circuit) {
|
|
|
|
|
// the circuit has probably been optimized away
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-12-30 17:53:46 +01:00
|
|
|
|
|
|
|
|
db::Net *net = circuit->net_by_cluster_id (cluster_id);
|
2018-12-30 22:37:31 +01:00
|
|
|
if (! net) {
|
|
|
|
|
// the net has probably been optimized away
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// follow the path up in the net hierarchy using the transformation and the upper cell index as the
|
|
|
|
|
// guide line
|
2019-01-06 12:53:22 +01:00
|
|
|
while (! inst_path.empty () && net->pin_count () > 0) {
|
2018-12-30 22:37:31 +01:00
|
|
|
|
|
|
|
|
cell_indexes.pop_back ();
|
|
|
|
|
|
2019-01-06 12:53:22 +01:00
|
|
|
const db::Pin *pin = circuit->pin_by_id (net->begin_pins ()->pin_id ());
|
2018-12-30 22:37:31 +01:00
|
|
|
tl_assert (pin != 0);
|
|
|
|
|
|
|
|
|
|
db::DCplxTrans dtrans = dbu_trans * inst_path.back ().complex_trans () * dbu_trans_inv;
|
|
|
|
|
|
|
|
|
|
// try to find a parent circuit which connects to this net
|
|
|
|
|
db::Circuit *upper_circuit = 0;
|
|
|
|
|
db::Net *upper_net = 0;
|
|
|
|
|
for (db::Circuit::refs_iterator r = circuit->begin_refs (); r != circuit->end_refs () && ! upper_net; ++r) {
|
|
|
|
|
if (r->trans ().equal (dtrans) && r->circuit () && r->circuit ()->cell_index () == cell_indexes.back ()) {
|
|
|
|
|
upper_net = r->net_for_pin (pin->id ());
|
|
|
|
|
upper_circuit = r->circuit ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (upper_net) {
|
|
|
|
|
circuit = upper_circuit;
|
|
|
|
|
net = upper_net;
|
|
|
|
|
inst_path.pop_back ();
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
return net;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-02 00:38:51 +01:00
|
|
|
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes)
|
2019-02-28 23:56:49 +01:00
|
|
|
{
|
|
|
|
|
// TODO: that's basically too much .. we only need the clusters
|
|
|
|
|
if (! m_netlist_extracted) {
|
|
|
|
|
throw tl::Exception (tl::to_string (tr ("The netlist has not been extracted yet")));
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
db::Layout &ly = dss ().layout (m_layout_index);
|
2019-02-28 23:56:49 +01:00
|
|
|
double dbu = ly.dbu ();
|
|
|
|
|
|
2019-03-03 18:10:52 +01:00
|
|
|
db::DeepLayer dl (&dss (), m_layout_index, ly.insert_layer ());
|
2019-02-28 23:56:49 +01:00
|
|
|
|
|
|
|
|
for (db::Layout::bottom_up_const_iterator cid = ly.begin_bottom_up (); cid != ly.end_bottom_up (); ++cid) {
|
|
|
|
|
|
|
|
|
|
const connected_clusters<db::PolygonRef> &clusters = m_net_clusters.clusters_per_cell (*cid);
|
|
|
|
|
if (clusters.empty ()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (connected_clusters<db::PolygonRef>::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) {
|
|
|
|
|
|
|
|
|
|
if (! clusters.is_root (*c)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db::Region rgate, rmetal;
|
|
|
|
|
|
2019-05-03 23:33:37 +02:00
|
|
|
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (gate), db::ICplxTrans (), rgate, 0);
|
|
|
|
|
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (metal), db::ICplxTrans (), rmetal, 0);
|
2019-02-28 23:56:49 +01:00
|
|
|
|
|
|
|
|
double agate = rgate.area () * dbu * dbu;
|
|
|
|
|
double ametal = rmetal.area () * dbu * dbu;
|
|
|
|
|
|
|
|
|
|
double r = ratio;
|
2019-03-01 23:07:28 +01:00
|
|
|
bool skip = false;
|
2019-02-28 23:56:49 +01:00
|
|
|
|
2019-03-02 00:38:51 +01:00
|
|
|
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
|
2019-02-28 23:56:49 +01:00
|
|
|
|
|
|
|
|
db::Region rdiode;
|
2019-05-03 23:33:37 +02:00
|
|
|
deliver_shapes_of_net_recursive (0, m_net_clusters, *cid, *c, layer_of (*d->first), db::ICplxTrans (), rdiode, 0);
|
2019-02-28 23:56:49 +01:00
|
|
|
|
2019-03-01 23:07:28 +01:00
|
|
|
if (fabs (d->second) < db::epsilon) {
|
|
|
|
|
if (rdiode.area () > 0) {
|
|
|
|
|
skip = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
r += rdiode.area () * dbu * dbu * d->second;
|
|
|
|
|
}
|
2019-02-28 23:56:49 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-01 23:07:28 +01:00
|
|
|
if (! skip) {
|
2019-02-28 23:56:49 +01:00
|
|
|
|
2019-03-01 23:07:28 +01:00
|
|
|
if (tl::verbosity () >= 50) {
|
|
|
|
|
tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r);
|
2019-02-28 23:56:49 +01:00
|
|
|
}
|
2019-03-01 23:07:28 +01:00
|
|
|
|
|
|
|
|
if (agate > dbu * dbu && ametal / agate > r + db::epsilon) {
|
|
|
|
|
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
|
|
|
|
|
for (db::Region::const_iterator r = rmetal.begin_merged (); ! r.at_end (); ++r) {
|
|
|
|
|
shapes.insert (*r);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-28 23:56:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return db::Region (new db::DeepRegion (dl));
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-20 20:30:12 +02:00
|
|
|
|
|
|
|
|
void db::LayoutToNetlist::save (const std::string &path, bool short_format)
|
|
|
|
|
{
|
|
|
|
|
tl::OutputStream stream (path);
|
|
|
|
|
db::LayoutToNetlistStandardWriter writer (stream, short_format);
|
|
|
|
|
set_filename (path);
|
|
|
|
|
writer.write (this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void db::LayoutToNetlist::load (const std::string &path)
|
|
|
|
|
{
|
|
|
|
|
tl::InputStream stream (path);
|
|
|
|
|
db::LayoutToNetlistStandardReader reader (stream);
|
|
|
|
|
set_filename (path);
|
|
|
|
|
set_name (stream.filename ());
|
|
|
|
|
reader.read (this);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-30 17:53:46 +01:00
|
|
|
}
|